@teispace/next-maker 4.0.1 → 5.0.0

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +119 -875
  3. package/dist/index.js +781 -1041
  4. package/package.json +9 -4
package/dist/index.js CHANGED
@@ -1,726 +1,537 @@
1
1
  #!/usr/bin/env node
2
- var ka=Object.defineProperty;var Ge=(e,t,r)=>()=>{if(r)throw r[0];try{return e&&(t=e(e=0)),t}catch(i){throw r=[i],i}};var Rr=(e,t)=>{for(var r in t)ka(e,r,{get:t[r],enumerable:!0})};import{existsSync as br}from"node:fs";import Ie from"node:fs/promises";import Hi from"node:path";var g,h,b,C,v,_,l,w=Ge(()=>{"use strict";g=async e=>Ie.readFile(e,"utf-8"),h=async(e,t)=>{await Ie.mkdir(Hi.dirname(e),{recursive:!0}),await Ie.writeFile(e,t,"utf-8")},b=async(e,t)=>{await Ie.mkdir(Hi.dirname(t),{recursive:!0}),await Ie.copyFile(e,t)},C=async e=>{br(e)&&await Ie.unlink(e)},v=async e=>{br(e)&&await Ie.rm(e,{recursive:!0,force:!0})},_=async(e,t)=>{let r=await g(e),i=JSON.parse(r),n=t(i);await h(e,JSON.stringify(n,null,2))},l=e=>br(e)});var qi={};Rr(qi,{PROJECT_PATHS:()=>a});var a,P=Ge(()=>{"use strict";a={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 rn={};Rr(rn,{cleanupHttpTypes:()=>Ue,injectBundleSentinel:()=>tn,installBundleSentinelMount:()=>tt,isFileUsed:()=>mc,isStringUsed:()=>dc,migrateClientUsages:()=>Dr,removeBundleSentinelMount:()=>rt,removeHttpExports:()=>Lr,resolveBundleSentinelLayoutPath:()=>jr,rewriteSentinelImports:()=>we,rewriteServerEntryImports:()=>Pe,stripBundleSentinel:()=>_t,stripSaveAuthTokens:()=>Or,updateConfigIndex:()=>Fr,updateHttpIndex:()=>Nr,updateTypesIndex:()=>_r,updateUtilsIndex:()=>Ar});import{execFile as ac}from"node:child_process";import D from"node:path";import{promisify as cc}from"node:util";async function kr(e,t,r){try{let{stdout:i}=await pc("grep",[...r,"--include=*.ts","--include=*.tsx",e,t]);return i}catch(i){if(i.code===1)return"";throw i}}var pc,Nr,Ar,_r,Fr,Ue,Or,Dr,jr,lc,Zi,Qi,tn,_t,tt,rt,Lr,mc,dc,we,Pe,en,Be=Ge(()=>{"use strict";P();w();pc=cc(ac);Nr=async(e,t)=>{let r=D.join(e,a.HTTP_UTILS,"index.ts");if(l(r)){let i=await g(r);i=i.replace(/export .* from '\.\/axios-client';?\n?/g,""),i=i.replace(/export .* from '\.\/fetch-client';?\n?/g,""),t.includes("fetch")&&!i.includes("fetch-client")&&(i+=`export * from './fetch-client';
3
- `),t.includes("axios")&&!i.includes("axios-client")&&(i+=`export * from './axios-client';
4
- `),await h(r,i)}},Ar=async e=>{let t=D.join(e,a.UTILS_INDEX);if(l(t)){let r=await g(t);r.includes("./http")||(r+=`export * from './http';
5
- `,await h(t,r))}},_r=async e=>{let t=D.join(e,a.TYPES_INDEX);if(l(t)){let r=await g(t);r.includes("./common")||(r+=`export * from './common';
6
- `),r.includes("./utility")||(r+=`export * from './utility';
7
- `),await h(t,r)}},Fr=async e=>{let t=D.join(e,a.CONFIG_INDEX);if(l(t)){let r=await g(t);r.includes("./app-apis")||(r+=`export * from './app-apis';
8
- `,await h(t,r))}},Ue=async(e,t)=>{let r=D.join(e,a.HTTP_TYPES);if(l(r)){let i=await g(r);t.includes("axios")||(i=i.replace(/declare module 'axios'\s*\{\s*export interface AxiosRequestConfig\s*\{[\s\S]*?\}\s*\}/g,""),i=`${i.replace(/\n\s*\n/g,`
2
+ import{Command as mp}from"commander";var Te={name:"@teispace/next-maker",version:"5.0.0",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 Ui}from"node:fs/promises";import Ct from"node:path";import A from"picocolors";var Xr=/@next-maker:([\w-]+)(?::(start|end))?/,Fs=/^\s*(?:\{\s*\/\*.*?\*\/\s*\}|\/\*.*?\*\/|\/\/.*|#.*)\s*$/,Ms=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]*$/,""),zt=(e,{off:t})=>{let r=e.split(`
3
+ `),n=[];for(let o=0;o<r.length;o++){let s=r[o],i=s.match(Xr);if(!i){n.push(s);continue}let[,a,p]=i,m=t.has(a);if(p==="start"){let d=_s(r,o+1,a);m&&(o=d);continue}if(p==="end")continue;let c=Fs.test(s);if(m){c&&o++;continue}c||n.push(Ms(s))}return n.join(`
4
+ `)},_s=(e,t,r)=>{for(let n=t;n<e.length;n++){let o=e[n].match(Xr);if(o&&o[1]===r&&o[2]==="end")return n}throw new Error(`Anchor @next-maker:${r}:start has no matching :end`)},Je=e=>e.includes("@next-maker:"),Yr=(e,t)=>{let r=e.split(`
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 p=a[2].split(",").map(c=>c.trim()).filter(Boolean),m=p.filter(c=>c.replace(/^type\s+/,"").split(/\s+as\s+/)[0]!==t);if(m.length===0)continue;if(m.length<p.length){n.push(`${a[1]}{ ${m.join(", ")} }${a[3]}`);continue}}n.push(i)}return n.join(`
6
+ `)},ze=(e,t)=>{let r=e.split(`
7
+ `),n=new RegExp(`^([ \\t]*)<${t}\\b`),o=-1,s="";for(let u=0;u<r.length;u++){let f=r[u].match(n);if(f){o=u,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,"\\$&"),p=new RegExp(`^${a}</${t}>\\s*$`),m=-1;for(let u=i+1;u<r.length;u++)if(p.test(r[u])){m=u;break}if(m===-1)throw new Error(`unwrapJsx: no closing </${t}> at the opening indent`);let c=r.slice(i+1,m).map(u=>u.startsWith(" ")?u.slice(2):u),d=[...r.slice(0,o),...c,...r.slice(m+1)].join(`
8
+ `);return Yr(d,t)},We=(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 p=n.slice(s+o[0].length,a);n=n.slice(0,s)+p+n.slice(a+1),r.lastIndex=s,o=r.exec(n)}return n};import{cp as on,mkdir as Js,mkdtemp as zs,readdir as rn,readFile as Oe,rm as Ht,rmdir as Ws,stat as Hs,writeFile as Ae}from"node:fs/promises";import{tmpdir as Ks}from"node:os";import N from"node:path";import{readdir as Ds}from"node:fs/promises";import gt from"node:path";var Qr=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}$`)},Ls=new Set(["node_modules",".git",".next","coverage","playwright-report","test-results"]),L=async(e,t=Ls)=>{let r=[],n=async o=>{let s;try{s=await Ds(o,{withFileTypes:!0})}catch{return}for(let i of s)if(i.isDirectory()){if(t.has(i.name))continue;await n(gt.join(o,i.name))}else i.isFile()&&r.push(gt.relative(e,gt.join(o,i.name)).split(gt.sep).join("/"))};return await n(e),r.sort()},se=(e,t)=>{let r=Qr(t);return e.filter(n=>r.test(n))};import{readFile as Us}from"node:fs/promises";import Gs from"node:path";var ht="next-maker.json",ge=".next-maker/overlays",Zr=1,G=e=>{throw new Error(`Invalid ${ht}: ${e}`)},tn=e=>{(typeof e!="object"||e===null)&&G("not an object");let t=e;t.manifestVersion!==Zr&&G(`manifestVersion ${String(t.manifestVersion)} is not supported by this CLI (expected ${Zr})`),(!t.starter?.name||!t.starter.version)&&G("starter.name and starter.version are required"),(!t.options||typeof t.options!="object")&&G("options is required"),(!t.features||typeof t.features!="object")&&G("features is required"),(!t.packageManagers||typeof t.packageManagers!="object")&&G("packageManagers is required");let r=t.options,n=t.features;for(let[o,s]of Object.entries(r))["choice","boolean","multi"].includes(s.type)||G(`option ${o} has unknown type ${String(s.type)}`),s.type!=="boolean"&&!Array.isArray(s.values)&&G(`option ${o} needs a values array`),s.default===void 0&&G(`option ${o} needs a default`);for(let[o,s]of Object.entries(n)){(!s.when||typeof s.when!="object")&&G(`feature ${o} needs a when`);for(let i of Object.keys(s.when))r[i]||G(`feature ${o} refers to unknown option ${i}`)}return{...t,always:{remove:t.always?.remove??[]}}},He=async e=>{let t=Gs.join(e,ht),r;try{r=await Us(t,"utf-8")}catch{throw new Error(`The starter at ${e} has no ${ht}. This CLI needs a starter that ships a composition manifest (nextjs-starter 2.x or later).`)}return tn(JSON.parse(r))},en=(e,t)=>e.some(r=>r===t),yt=(e,t,r)=>t===void 0?!1:e?.type==="multi"&&Array.isArray(t)?t.some(n=>en(r,n)):en(r,t),ie=(e,t,r)=>Object.entries(t.when).every(([n,o])=>yt(e.options[n],r[n],o)),Bs=e=>e.type==="boolean"?!1:e.type==="multi"?[]:e.default,Vs=(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},Ke=(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:Vs(i,s,a)}for(let[s,i]of Object.entries(e.options))if(i.requires)for(let[a,p]of Object.entries(i.requires)){if(yt(e.options[a],r[a],p))continue;let m=Bs(i);r[s]!==m&&JSON.stringify(r[s])!==JSON.stringify(m)&&(n.push({option:s,value:m,reason:`${s} requires ${a} to be ${p.map(String).join(" or ")}`}),r[s]=m)}return{answers:r,forced:n,unknown:o}};var ae=(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}`}},qs=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[]}},wt=(e,t)=>{if(t==="pnpm")return e;let r=e;for(let[n,o]of qs(t))r=r.replace(n,o);return r},Wt=e=>e==="package.json"||e.startsWith(".husky/")||e.endsWith(".md")||e===".lintstagedrc.mjs";var he=e=>[...new Set(e)],Xt=(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=[],p=[];for(let[c,d]of Object.entries(e.features)){let u=ie(e,d,t);(u?o:s).push(c);let f=u?d.on:d.off;if(!f)continue;i.push(f);let h=Object.keys(d.when);f.overlay&&!a.some(w=>w.name===f.overlay)&&a.push({name:f.overlay,options:h}),f.remove?.length&&p.push({feature:c,options:h,patterns:f.remove})}let m=he([...e.always.remove,...n.remove??[]]);return{answers:t,packageManager:r,pmSpec:n,featuresOn:o,featuresOff:s,pmOverlays:n.overlay?[n.overlay]:[],featureOverlays:a,removePatterns:he([...m,...p.flatMap(c=>c.patterns)]),unconditionalRemovals:m,featureRemovals:p,anchorsOff:new Set(i.flatMap(c=>c.anchors??[])),unwrapJsx:i.flatMap(c=>c.unwrapJsx??[]),unwrapCall:i.flatMap(c=>c.unwrapCall??[]),packages:he(i.flatMap(c=>c.packages??[])),devPackages:he(i.flatMap(c=>c.devPackages??[])),scripts:he(i.flatMap(c=>c.scripts??[])),env:he(i.flatMap(c=>c.env??[])),packageJsonKeys:he(i.flatMap(c=>c.packageJsonKeys??[]))}},Xs=new Set([".ts",".tsx",".js",".mjs",".cjs",".css",".json",".yml",".yaml",".md",".sh",".example",".mdx"]),Ys=e=>Xs.has(N.extname(e))||e.startsWith(".husky/")||e==="Dockerfile",nn=async(e,t,r)=>{let n=N.join(e,r),o=await L(n,new Set);if(o.length===0)throw new Error(`Overlay "${r}" is missing or empty at ${ge}/${r}`);for(let s of o){let i=N.join(t,s);await Js(N.dirname(i),{recursive:!0}),await on(N.join(n,s),i,{force:!0})}return o},Yt=async(e,t=e)=>{let r;try{r=await rn(t,{withFileTypes:!0})}catch{return}for(let n of r)n.isDirectory()&&n.name!=="node_modules"&&n.name!==".git"&&await Yt(e,N.join(t,n.name));t!==e&&(await rn(t)).length===0&&await Ws(t)},sn=(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,p)=>a?.[p],e);i&&typeof i=="object"&&Object.keys(i).length===0&&sn(e,s)}},Qs=async(e,t,r)=>{let n=N.join(e,"package.json"),o=JSON.parse(await Oe(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)sn(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=>ae(t.packageManager,a)).join(" && ")}if(o.scripts)for(let[i,a]of Object.entries(o.scripts))typeof a=="string"&&(o.scripts[i]=wt(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 Ae(n,`${JSON.stringify(o,null,2)}
9
+ `,"utf-8")},Zs=async(e,t)=>{let r=N.join(e,".env.example"),n;try{n=await Oe(r,"utf-8")}catch{return}for(let o of t)n=n.replace(new RegExp(`^${o}=.*\\n?`,"gm"),"");await Ae(r,n.replace(/\n{3,}/g,`
10
+
11
+ `),"utf-8")},Qt=async(e,t,r)=>{let n={removed:[],overlaid:[],anchored:[],unwrapped:[]},o=N.join(e,ge),s=await zs(N.join(Ks(),"next-maker-overlays-"));try{await Kt(o)&&await on(o,s,{recursive:!0});for(let m of r.pmOverlays)n.overlaid.push(...await nn(s,e,m));let a=await L(e),p=new Set(r.removePatterns.flatMap(m=>se(a,m)));for(let m of p)await Ht(N.join(e,m),{force:!0}),n.removed.push(m);for(let m of r.featureOverlays){let c=await nn(s,e,m.name),d=[...r.unconditionalRemovals,...r.featureRemovals.filter(f=>!f.options.some(h=>m.options.includes(h))).flatMap(f=>f.patterns)],u=new Set(d.flatMap(f=>se(c,f)));for(let f of u)await Ht(N.join(e,f),{force:!0}),n.removed.push(f);n.overlaid.push(...c.filter(f=>!u.has(f)))}}finally{await Ht(s,{recursive:!0,force:!0})}await Yt(e);let i=await L(e);for(let a of i){if(!Ys(a))continue;let p=N.join(e,a),m=await Oe(p,"utf-8");Je(m)&&(await Ae(p,zt(m,{off:r.anchorsOff}),"utf-8"),n.anchored.push(a))}for(let{file:a,tag:p}of r.unwrapJsx){let m=N.join(e,a);if(!await Kt(m))continue;let c=await Oe(m,"utf-8"),d=ze(c,p);d!==c&&(await Ae(m,d,"utf-8"),n.unwrapped.push(`${a}:<${p}>`))}for(let{file:a,name:p}of r.unwrapCall){let m=N.join(e,a);if(!await Kt(m))continue;let c=await Oe(m,"utf-8"),d=We(c,p);d!==c&&(await Ae(m,d,"utf-8"),n.unwrapped.push(`${a}:${p}()`))}if(await Qs(e,r,t),await Zs(e,r.env),r.packageManager!=="pnpm")for(let a of await L(e)){if(!Wt(a)||a==="package.json")continue;let p=N.join(e,a),m=await Oe(p,"utf-8"),c=wt(m,r.packageManager);c!==m&&await Ae(p,c,"utf-8")}return n},Kt=async e=>{try{return await Hs(e),!0}catch{return!1}};import{readFile as ei}from"node:fs/promises";import ti from"node:path";var Zt=async e=>{let t=ti.resolve(process.cwd(),e),r;try{r=await ei(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},ye=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 er={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}}},Ie=()=>Object.keys(er),Ne=e=>{let t=er[e];if(!t)throw new Error(`Unknown preset "${e}". Valid: ${Ie().join(", ")}.`);return{...t.answers}};var Xe=new Set;function xt(e){return Xe.add(e),()=>Xe.delete(e)}function an(e){let{logger:t=p=>console.error(p),exitOnCancel:r=process.env.NODE_ENV!=="test"}=e??{},n=!1,o=async()=>{let p=[...Xe];Xe.clear();for(let m of p)try{await m()}catch(c){t(`Cleanup failed: ${c instanceof Error?c.message:String(c)}`)}},s=()=>{if(n){r&&process.exit(130);return}n=!0;let p=Xe.size>0;o().then(()=>{console.log(""),t("Setup cancelled by user"),console.log(""),r&&process.exit(p?130:0)})},i=p=>{if(p!==null&&typeof p=="object"&&"code"in p&&p.code==="ERR_USE_AFTER_CLOSE"){s();return}if(p instanceof Error){t(`Uncaught exception: ${p.message}`);return}t(`Uncaught exception: ${String(p)}`)},a=p=>{if(p instanceof Error){t(`Unhandled promise rejection: ${p.message}`);return}t(`Unhandled promise rejection: ${String(p)}`)};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 z from"picocolors";var ri={reset:e=>e,red:z.red,green:z.green,yellow:z.yellow,blue:z.blue,cyan:z.cyan,magenta:z.magenta,white:z.white,gray:z.gray,bright:z.bold,dim:z.dim};function ce(e,t="reset"){let r=ri[t]??(n=>n);console.log(r(e))}function bt(e){ce(`\u2716 ${e}`,"red")}var x=bt;function cn(e){ce(`\u26A0 ${e}`,"yellow")}function l(e){ce(e)}function vt(){console.log(""),ce("\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"),ce("\u2551 \u2551","cyan"),ce("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),ce("\u2551 \u2551","cyan"),ce("\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 ni from"ora";function $t(e="",t){let r=ni({text:e,...t});return r.start(),r}var oi=e=>e.charAt(0).toUpperCase()+e.slice(1),U=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),$=e=>oi(U(e));import si from"ora";var g=si();import{execFile as ii}from"node:child_process";import{cp as pn,mkdir as ai,stat as ci}from"node:fs/promises";import Ye from"node:path";import{promisify as pi}from"node:util";import li from"degit";var mi=pi(ii),ln="teispace/nextjs-starter",Pt="v2.0.0-alpha.0",di="NEXT_MAKER_STARTER_PATH",ui=(e=Pt)=>e==="main"?ln:`${ln}#${e}`,B=(e={})=>{let t=e.starterPath??process.env[di];return t?{kind:"local",location:Ye.resolve(t)}:{kind:"remote",location:ui(e.ref)}},fi=new Set(["node_modules",".git",".next","coverage","playwright-report","test-results",".env"]),kt=async(e,t={})=>{let r=t.source??B();if(r.kind==="local"){try{if(!(await ci(r.location)).isDirectory())throw new Error("not a directory")}catch{throw new Error(`Starter path ${r.location} is not a directory`)}return await gi(r.location,e),r}return await li(r.location,{cache:!1,force:!0,verbose:t.verbose??!1}).clone(e),r},gi=async(e,t)=>{let r=null;try{let{stdout:n}=await mi("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=>!fi.has(Ye.basename(n))});return}for(let n of r){let o=Ye.join(t,n);await ai(Ye.dirname(o),{recursive:!0});try{await pn(Ye.join(e,n),o)}catch{}}};import{existsSync as tr}from"node:fs";import Qe from"node:fs/promises";import hi from"node:path";var j=async e=>Qe.readFile(e,"utf-8"),k=async(e,t)=>{await Qe.mkdir(hi.dirname(e),{recursive:!0}),await Qe.writeFile(e,t,"utf-8")};var mn=async e=>{tr(e)&&await Qe.unlink(e)},Ze=async e=>{tr(e)&&await Qe.rm(e,{recursive:!0,force:!0})},dn=async(e,t)=>{let r=await j(e),n=JSON.parse(r),o=t(n);await k(e,JSON.stringify(o,null,2))},y=e=>tr(e);import{execFile as yi}from"node:child_process";import{promisify as wi}from"node:util";var xi=wi(yi),Z=(e,t)=>xi("git",e,t?{cwd:t}:void 0);function bi(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 St=async(e,t,r)=>{try{if(await Z(["init"],e),await Z(["add","."],e),await Z(["commit","-m","Initial commit from @teispace/next-maker"],e),t&&(bi(t),await Z(["remote","add","origin",t],e),r)){await Z(["fetch","origin"],e);let n="main";try{await Z(["show-branch","origin/main"],e)}catch{try{await Z(["show-branch","origin/master"],e),n="master"}catch{n="main"}}await Z(["merge",`origin/${n}`,"--allow-unrelated-histories","-X","ours"],e),await Z(["push","origin",`HEAD:${n}`],e)}}catch(n){console.warn("Failed to initialize git repository",n)}};import{execFile as vi}from"node:child_process";import{promisify as $i}from"node:util";var Pi=$i(vi),un=(e,t,r)=>Pi(e,t,{cwd:r,maxBuffer:64*1024*1024}),ki=e=>{let t=e;return(`${t.stdout??""}
12
+ ${t.stderr??""}`.trim()||(t.message??String(e))).split(`
13
+ `).filter(Boolean).slice(-12).join(`
14
+ `)},Si=e=>{switch(e){case"pnpm":return["install","--no-frozen-lockfile"];case"yarn":return["install","--no-immutable"];default:return["install"]}},pe=async(e,t)=>{try{await un(t,Si(t),e)}catch(r){throw new Error(`Failed to install dependencies with ${t}:
15
+ ${ki(r)}`,{cause:r})}},Et=async(e,t,r)=>{let n=t==="npm"?["run",r]:[r];try{await un(t,n,e)}catch(o){console.warn(`Warning: Failed to run script '${r}': ${o}`)}};import Ei from"enquirer";var{prompt:fn}=Ei,Ri=/^[a-z0-9-_]+$/,ji=/^\d+\.\d+\.\d+$/,gn=e=>Ri.test(e),hn=e=>ji.test(e),et=e=>{if(!gn(e))throw new Error(`Invalid project name "${e}". Use lowercase letters, digits, hyphens, and underscores only \u2014 no slashes, "..", or absolute paths.`)},le=(e,t={})=>{let r=t.projectName??e??"my-app";et(r);let n=t.version??"0.1.0";if(!hn(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}},yn=e=>e.state?.answers??e.enquirer?.answers??{},wn=async e=>{e!==void 0&&et(e);let t=await fn([{type:"input",name:"projectName",message:"What is the project name?",initial:e||"my-app",skip:!!e,validate:r=>gn(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=>hn(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!yn(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 le(e,{...t,projectName:e??t.projectName})},Rt=async(e,t={})=>{let r=Object.entries(e.options).map(([s,i])=>{let a=t[s]??i.default,p={name:s,message:i.label??s,skip:function(){if(!i.requires)return!1;let c=yn(this);return!Object.entries(i.requires).every(([d,u])=>yt(e.options[d],c[d],u))}};if(i.type==="boolean")return{...p,type:"confirm",initial:!!a};if(i.type==="multi")return{...p,type:"multiselect",choices:(i.values??[]).map(c=>({name:c,value:c})),initial:Array.isArray(a)?a:[]};let m=i.values??[];return{...p,type:"select",choices:m,initial:Math.max(0,m.indexOf(String(a)))}}),n=await fn(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 Ci from"node:path";var C={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 Ti=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}},xn=async(e,t)=>{let{remote:r,homepage:n,issues:o}=Ti(t);await dn(Ci.join(e,C.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 _i}from"node:fs/promises";import de from"node:path";import{cp as Oi,mkdir as Ai,mkdtemp as Ii,readFile as we,rm as $n,writeFile as jt}from"node:fs/promises";import{tmpdir as Ni}from"node:os";import O from"node:path";var Fe=".next-maker.json",me=async(e,t)=>{await jt(O.join(e,Fe),`${JSON.stringify(t,null,2)}
16
+ `,"utf-8")},ee=async e=>{let t=O.join(e,Fe);if(!y(t))throw new Error(`No ${Fe} in ${e}. Only projects created with next-maker 5+ can be reconfigured; run \`next-maker init\` for a new project.`);return JSON.parse(await we(t,"utf-8"))},Me=async(e,t={})=>{let r=e.starter.source.startsWith("/")&&y(e.starter.source)?e.starter.source:void 0,n=B({starterPath:t.starterPath??r}),o=await Ii(O.join(Ni(),"next-maker-starter-"));await kt(o,{source:n});let s=await He(o),i=await L(o);return{dir:o,source:n,manifest:s,files:i,dispose:()=>$n(o,{recursive:!0,force:!0})}},bn=(e,t)=>e[t]??{},Fi=e=>[...e.off?.anchors??[],...e.on?.anchors??[]],Mi=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 we(O.join(e.dir,o),"utf-8")}catch{continue}if(!Je(s))continue;let i=s.split(`
17
+ `),a=[];for(let p=0;p<i.length;p++){let m=i[p].match(n);if(m)if(m[1]==="start"){let c=p+1;for(;c<i.length&&!/@next-maker:[\w-]+:end/.test(i[c]);)a.push(i[c++]);p=c}else m[1]==="end"||(/^\s*(\{\s*\/\*|\/\*|\/\/|#)/.test(i[p])?i[p+1]!==void 0&&a.push(i[++p]):a.push(i[p].replace(/\s*(\{\s*\/\*|\/\*|\/\/|#).*$/,"")))}a.length&&r.push({file:o,lines:a})}return r},Pn=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=bn(r,"off"),o=bn(r,"on"),s=new Set(e.manifest.always.remove.flatMap(p=>se(e.files,p))),i=[...new Set((n.remove??[]).flatMap(p=>se(e.files,p)))].filter(p=>!s.has(p)).sort(),a=o.overlay?(await L(O.join(e.dir,ge,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 Mi(e,Fi(r))}},kn=e=>/\.(test|spec)\.tsx?$/.test(e)||e.startsWith("test/")||e.includes("__test-utils__"),rr=async(e,t,r)=>{let n=JSON.parse(await we(O.join(e,"package.json"),"utf-8")),o=t.answers.tests!==!1,s=[],i=Object.values(r.manifest.features).filter(p=>!ie(r.manifest,p,t.answers)).flatMap(p=>p.off?.remove??[]),a=new Set(i.flatMap(p=>se(r.files,p)));for(let[p,m]of Object.entries(r.manifest.features)){let c=ie(r.manifest,m,t.answers);if(!c){s.push({id:p,on:c,drift:[]});continue}let d=await Pn(r,p),u=[];for(let f of[...d.files,...d.overlayFiles])!o&&kn(f)||a.has(f)||y(O.join(e,f))||u.push({kind:"missingFile",file:f});for(let f of d.packages)n.dependencies?.[f]||u.push({kind:"missingPackage",name:f,dev:!1});for(let f of d.devPackages)n.devDependencies?.[f]||u.push({kind:"missingPackage",name:f,dev:!0});for(let f of d.scripts)n.scripts?.[f]||u.push({kind:"missingScript",name:f});s.push({id:p,on:c,drift:u})}return s},Sn=async(e,t,r,n)=>{let o=t.answers,s=JSON.parse(await we(O.join(r.dir,"package.json"),"utf-8")),i=[],a=[];for(let[c,d]of Object.entries(r.manifest.features)){let u=ie(r.manifest,d,o),f=ie(r.manifest,d,n);u!==f&&(f?i:a).push(await Pn(r,c))}let p=n.tests!==!1,m={before:o,after:n,turningOn:i,turningOff:a,copy:[],remove:[],addPackages:{},addDevPackages:{},removePackages:[],addScripts:{},removeScripts:[],unwrapJsx:[],unwrapCall:[],manual:[]};for(let c of a){for(let d of[...c.files,...c.overlayFiles])y(O.join(e,d))&&m.remove.push(d);m.removePackages.push(...c.packages,...c.devPackages),m.removeScripts.push(...c.scripts),m.unwrapJsx.push(...c.feature.off?.unwrapJsx??[]),m.unwrapCall.push(...c.feature.off?.unwrapCall??[]),c.snippets.length&&m.manual.push({feature:c.id,direction:"delete",snippets:c.snippets})}for(let c of i){for(let d of[...c.files,...c.overlayFiles])!p&&kn(d)||(!y(O.join(e,d))||c.overlayFiles.includes(d))&&m.copy.push(d);for(let d of c.packages)m.addPackages[d]=s.dependencies?.[d]??"latest";for(let d of c.devPackages)m.addDevPackages[d]=s.devDependencies?.[d]??"latest";for(let d of c.scripts)s.scripts?.[d]&&(m.addScripts[d]=s.scripts[d]);c.snippets.length&&m.manual.push({feature:c.id,direction:"add",snippets:c.snippets})}return m.copy=[...new Set(m.copy)].sort(),m.remove=[...new Set(m.remove)].sort(),m},En=async(e,t,r,n)=>{for(let i of r.remove)await $n(O.join(e,i),{force:!0});for(let i of r.copy){let a=r.turningOn.find(c=>c.overlayFiles.includes(i)),p=a?.feature.on?.overlay?O.join(t.dir,ge,a.feature.on.overlay,i):O.join(t.dir,i),m=O.join(e,i);await Ai(O.dirname(m),{recursive:!0}),await Oi(p,m,{force:!0})}for(let{file:i,tag:a}of r.unwrapJsx){let p=O.join(e,i);if(!y(p))continue;let m=await we(p,"utf-8"),c=ze(m,a);c!==m&&await jt(p,c,"utf-8")}for(let{file:i,name:a}of r.unwrapCall){let p=O.join(e,i);if(!y(p))continue;let m=await we(p,"utf-8"),c=We(m,a);c!==m&&await jt(p,c,"utf-8")}let o=O.join(e,"package.json"),s=JSON.parse(await we(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=vn(s.dependencies),s.devDependencies=vn(s.devDependencies),await jt(o,`${JSON.stringify(s,null,2)}
18
+ `,"utf-8"),await me(e,{...n,answers:r.after})},vn=e=>Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)));var Di=(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),Rn=async(e,t)=>{let r=[de.join(e,C.GITHUB_DIR,C.GITHUB_PR_TEMPLATE),de.join(e,C.CODE_OF_CONDUCT),de.join(e,C.CONTRIBUTING),de.join(e,C.SECURITY)],n=de.join(e,C.GITHUB_DIR,C.GITHUB_ISSUE_TEMPLATE);if(y(n))for(let o of await _i(n))r.push(de.join(n,o));for(let o of r){if(!y(o))continue;let s=await j(o),i=Di(s,t);i!==s&&await k(o,i)}},jn=async(e,t)=>{let r=de.join(e,C.ENV_EXAMPLE);if(!y(r))return;let n=await j(r);n=n.replace(/^CONTAINER_NAME=.*$/m,`CONTAINER_NAME=${t.projectName} # -public`),n=n.replace(/^IMAGE_NAME=.*$/m,`IMAGE_NAME=${t.projectName} # -public`),await k(r,n)},Li=(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)}`},Cn=async(e,t,r,n,o)=>{let s=de.join(e,C.README);if(!t.readme){await mn(s);return}let i=Object.entries(n).filter(([m])=>m!=="packageManager").map(([m,c])=>Li(r,m,c)).filter(m=>m!==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 p=`# ${t.projectName}
9
19
 
10
- `).trim()}
11
- `,await h(r,i))}},Or=e=>{let t=e;return t=t.replace(/\/\*\*(?:[^*]|\*(?!\/))*?\*\/\n(?=(?:export\s+)?const\s+BEARER_ALLOWED_ENVS\b)/,""),t=t.replace(/^(?:export\s+)?const\s+BEARER_ALLOWED_ENVS\s*=[\s\S]*?;\n?/m,""),t=t.replace(/^export const SAVE_AUTH_TOKENS\s*=.*;\n?/m,""),t},Dr=async(e,t,r)=>{if(t===r)return;let i=t==="fetch"?"fetchClient":"axiosClient",n=r==="fetch"?"fetchClient":"axiosClient",o=t==="fetch"?"fetch-client":"axios-client",s=r==="fetch"?"fetch-client":"axios-client",c=D.join(e,"src");try{let p=await kr(i,c,["-r","-l"]);if(!p.trim())return;let u=p.trim().split(`
12
- `).filter(d=>d);for(let d of u){if(!l(d))continue;let f=await g(d),x=!1,I=new RegExp(`(import\\s*\\{[^}]*?)\\b${i}\\b([^}]*?\\}\\s*from\\s*['"][^'"]*?/http['"])`,"g");I.test(f)&&(f=f.replace(I,`$1${n}$2`),x=!0);let H=new RegExp(`import\\s+${i}\\s+from\\s+['"]([^'"]*?)/${o}['"]`,"g");H.test(f)&&(f=f.replace(H,`import ${n} from '$1/${s}'`),x=!0);let k=new RegExp(`\\b${i}\\b(?=\\.)`,"g");k.test(f)&&(f=f.replace(k,n),x=!0);let Z=new RegExp(`\\b${i}\\b(?![.\\w])`,"g");Z.test(f)&&(f=f.replace(Z,n),x=!0),x&&await h(d,f)}}catch(p){console.warn("Warning: Could not scan for HTTP client usages:",p)}},jr=e=>{let t=D.join(e,a.LOCALE_LAYOUT);if(l(t))return t;let r=D.join(e,a.ROOT_LAYOUT);return l(r)?r:null},lc="// Regression sentinel \u2014 see file comment for what this guards.",Zi="import { HttpClientBundleSentinel } from '@/lib/utils/http/__bundle-sentinel__/client-bundle-sentinel';",Qi="<HttpClientBundleSentinel />",tn=e=>{if(e.includes(Qi))return e;let t=e;if(!t.includes(Zi)){let o=/(^import\s[^\n]*\n)+/m,s=t.match(o);if(!s)throw new Error("injectBundleSentinel: no `import` lines found \u2014 expected a layout file with at least one import.");let c=t.slice(0,(s.index??0)+s[0].length),p=t.slice((s.index??0)+s[0].length);t=`${c}${lc}
13
- ${Zi}
14
- ${p}`}let r=/(<RootProvider\b[^>]*>)\n([ \t]*)/,i=t.match(r);if(!i)throw new Error("injectBundleSentinel: could not locate `<RootProvider \u2026>` opening tag \u2014 layout shape unexpected.");let n=i[2];return t=t.replace(r,`$1
15
- ${n}${Qi}
16
- ${n}`),t},_t=e=>{let t=e,r=/@\/lib\/utils\/http\/__bundle-sentinel__\/client-bundle-sentinel/,i=new RegExp(`(?:^[ \\t]*//[^\\n]*\\n)?^[ \\t]*import[^\\n]*${r.source}[^\\n]*\\n`,"m");return t=t.replace(i,""),t=t.replace(/[ \t]*<HttpClientBundleSentinel\s*\/>\s*\n/,""),t},tt=async e=>{let t=jr(e);if(!t)return;let r=await g(t),i=tn(r);i!==r&&await h(t,i)},rt=async e=>{let t=jr(e);if(!t)return;let r=await g(t),i=_t(r);i!==r&&await h(t,i)},Lr=async e=>{let t=D.join(e,a.UTILS_INDEX);if(l(t)){let i=await g(t);i=i.replace(/^\s*export\s+\*\s+from\s+['"]\.\/http['"];?.*$/gm,""),await h(t,i)}let r=D.join(e,a.TYPES_INDEX);if(l(r)){let i=await g(r),n=D.join(e,a.COMMON_TYPES_DIR);if(!l(n))i=i.replace(/^\s*export\s+\*\s+from\s+['"]\.\/common['"];?.*$/gm,"");else{let o=D.join(n,"index.ts");if(l(o)){let s=await g(o);s=s.replace(/^\s*export\s+\*\s+from\s+['"]\.\/http\.types['"];?.*$/gm,""),await h(o,s)}}l(D.join(e,a.UTILITY_TYPES_DIR))||(i=i.replace(/^\s*export\s+\*\s+from\s+['"]\.\/utility['"];?.*$/gm,"")),await h(r,i)}},mc=async(e,t,r=[])=>{let i=D.join(e,"src"),o=`from ['"].*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}.*['"]`;try{let s=await kr(o,i,["-r","-E"]);return s.trim()?s.trim().split(`
17
- `).filter(u=>{let[d]=u.split(":"),f=D.resolve(e,d);return!r.some(x=>!!(f===x||f.startsWith(x+D.sep)))}).length>0:!1}catch(s){return console.warn(`Warning: Failed to check usage for ${t}:`,s),!0}},dc=async(e,t,r=[])=>{let i=D.join(e,"src");try{let n=await kr(t,i,["-r","-F"]);return n.trim()?n.trim().split(`
18
- `).filter(c=>{let[p]=c.split(":"),u=D.resolve(e,p);return!r.some(d=>!!(u===d||u.startsWith(d+D.sep)))}).length>0:!1}catch(n){return console.warn(`Warning: Failed to check usage for string ${t}:`,n),!0}},we=async(e,t)=>{let r=D.join(e,a.HTTP_BUNDLE_SENTINEL_FILE);if(!l(r))return;let i=await g(r),n=["toSearchParams"];if(t.includes("axios")&&n.unshift("axiosClient","createAxiosClient"),t.includes("fetch")){let c=n.indexOf("toSearchParams");n.splice(c,0,"createFetchClient","fetchClient")}n.sort();let o=`import {
19
- ${n.join(`,
20
- `)},
21
- } from '@/lib/utils/http';`;i=i.replace(/import\s*\{[^}]*\}\s*from\s*['"]@\/lib\/utils\/http['"];/,o);let s=n.map(c=>` ${c},`).join(`
22
- `);i=i.replace(/const __sentinel__ = \{[\s\S]*?\};/,`const __sentinel__ = {
23
- ${s}
24
- };`),await h(r,i)},Pe=async(e,t)=>{let r=D.join(e,a.HTTP_SERVER_FILE);if(!l(r)||t.length===0)return;let i=await g(r),n=o=>{i=i.replace(o,"")};t.includes("axios")||(n(/^import\s*\{\s*createAxiosClient\s*\}\s*from\s*['"]\.\/axios-client['"];\n?/m),n(/export const axiosClient = createAxiosClient\(\{[\s\S]*?\}\);\n?\n?/),i=en(i,"createAxiosClient")),t.includes("fetch")||(n(/^import\s*\{\s*createFetchClient\s*\}\s*from\s*['"]\.\/fetch-client['"];\n?/m),n(/export const fetchClient = createFetchClient\(\{[\s\S]*?\}\);\n?\n?/),i=en(i,"createFetchClient")),i=i.replace(/\n{3,}/g,`
25
-
26
- `),await h(r,i)},en=(e,t)=>{let r=/^export\s*\{([^}]*)\};\n?/m;return e.replace(r,(i,n)=>{let o=n.split(",").map(s=>s.trim()).filter(s=>s.length>0&&s!==t);return o.length===0?"":`export { ${o.join(", ")} };
27
- `})}});import{existsSync as np}from"node:fs";import{access as op}from"node:fs/promises";import _n from"node:path";var Se,Ht,Fn=Ge(()=>{"use strict";Se=async(e,t,r)=>{try{return await op(_n.join(e,r,t)),!0}catch{return!1}},Ht=(e,t,r)=>np(_n.join(e,t,r))});import{access as Ut,readFile as sp}from"node:fs/promises";import at from"node:path";var M,On=Ge(()=>{"use strict";M=async e=>{try{let t=at.join(e,"package.json"),r=await sp(t,"utf-8"),i=JSON.parse(r),n={...i.dependencies,...i.devDependencies},o=!!(n["@reduxjs/toolkit"]&&n["react-redux"]),s=!!n["next-intl"],c=!!n.axios,p=!1,u=at.join(e,"src","lib","utils","http","axios-client");try{await Ut(u)}catch{c=!1}let d=at.join(e,"src","lib","utils","http","fetch-client");try{await Ut(d),p=!0}catch{p=!1}let f;c&&p?f="both":c?f="axios":p?f="fetch":f="none";let x=!!n.vitest;if(!x)try{await Ut(at.join(e,"vitest.config.ts")),x=!0}catch{}let I="none";if(n["socket.io-client"])try{await Ut(at.join(e,"src","lib","utils","ws")),I="present"}catch{}return{hasRedux:o,httpClient:f,hasI18n:s,hasTests:x,ws:I}}catch(t){throw new Error(`Failed to detect project setup: ${t}`,{cause:t})}}});var ct={};Rr(ct,{detectProjectSetup:()=>M,directoryExists:()=>Se,fileExistsAt:()=>Ht});var q=Ge(()=>{"use strict";Fn();On()});import{Command as Td}from"commander";var Di={name:"@teispace/next-maker",version:"4.0.1",description:"Create a new Teispace Next.js application with interactive setup.",keywords:["nextjs","react","typescript","tailwindcss","starter","template","redux"],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"},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 Hr from"node:path";import ee from"picocolors";var Ze=new Set;function ji(e){return Ze.add(e),()=>Ze.delete(e)}function Li(e){let{logger:t=p=>console.error(p),exitOnCancel:r=process.env.NODE_ENV!=="test"}=e??{},i=!1,n=async()=>{let p=[...Ze];Ze.clear();for(let u of p)try{await u()}catch(d){t(`Cleanup failed: ${d instanceof Error?d.message:String(d)}`)}},o=()=>{if(i){r&&process.exit(130);return}i=!0;let p=Ze.size>0;n().then(()=>{console.log(""),t("Setup cancelled by user"),console.log(""),r&&process.exit(p?130:0)})},s=p=>{if(p!==null&&typeof p=="object"&&"code"in p&&p.code==="ERR_USE_AFTER_CLOSE"){o();return}if(p instanceof Error){t(`Uncaught exception: ${p.message}`);return}t(`Uncaught exception: ${String(p)}`)},c=p=>{if(p instanceof Error){t(`Unhandled promise rejection: ${p.message}`);return}t(`Unhandled promise rejection: ${String(p)}`)};return process.on("SIGINT",o),process.on("SIGTERM",o),process.on("uncaughtException",s),process.on("unhandledRejection",c),()=>{process.off("SIGINT",o),process.off("SIGTERM",o),process.off("uncaughtException",s),process.off("unhandledRejection",c)}}import ae from"picocolors";var Aa={reset:e=>e,red:ae.red,green:ae.green,yellow:ae.yellow,blue:ae.blue,cyan:ae.cyan,magenta:ae.magenta,white:ae.white,gray:ae.gray,bright:ae.bold,dim:ae.dim};function xe(e,t="reset"){let r=Aa[t]??(i=>i);console.log(r(e))}function Nt(e){xe(`\u2716 ${e}`,"red")}var E=Nt;function Mi(e){xe(`\u26A0 ${e}`,"yellow")}function m(e){xe(e)}function Gi(){console.log(""),xe("\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"),xe("\u2551 \u2551","cyan"),xe("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),xe("\u2551 \u2551","cyan"),xe("\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 _a from"ora";function S(e="",t){let r=_a({text:e,...t});return r.start(),r}var Fa=e=>e.charAt(0).toUpperCase()+e.slice(1),G=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),N=e=>Fa(G(e));import Oa from"ora";var y=Oa();w();import{execFile as Da}from"node:child_process";import{promisify as ja}from"node:util";var La=ja(Da),ue=(e,t)=>La("git",e,t?{cwd:t}:void 0);function Ma(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 Ui=async(e,t,r)=>{try{if(await ue(["init"],e),await ue(["add","."],e),await ue(["commit","-m","Initial commit from @teispace/next-maker"],e),t&&(Ma(t),await ue(["remote","add","origin",t],e),r)){await ue(["fetch","origin"],e);let i="main";try{await ue(["show-branch","origin/main"],e)}catch{try{await ue(["show-branch","origin/master"],e),i="master"}catch{i="main"}}await ue(["merge",`origin/${i}`,"--allow-unrelated-histories","-X","ours"],e),await ue(["push","origin",`HEAD:${i}`],e)}}catch(i){console.warn("Failed to initialize git repository",i)}};import{execFile as Ga}from"node:child_process";import{promisify as Ha}from"node:util";var Ua=Ha(Ga),Qe=(e,t,r)=>Ua(e,t,{cwd:r}),Bi=async(e,t)=>{try{await Qe(t,["install"],e)}catch(r){throw new Error(`Failed to install dependencies with ${t}: ${r}`,{cause:r})}},K=async(e,t,r)=>{let i=t==="npm"?["run",r]:[r];try{await Qe(t,i,e)}catch(n){console.warn(`Warning: Failed to run script '${r}': ${n}`)}},Ba=()=>{let e=process.env.npm_config_user_agent;if(e){if(e.startsWith("yarn"))return"yarn";if(e.startsWith("pnpm"))return"pnpm";if(e.startsWith("bun"))return"bun"}return"npm"},A=async e=>{let{existsSync:t}=await import("node:fs"),r=await import("node:path");return t(r.join(e,"pnpm-lock.yaml"))?"pnpm":t(r.join(e,"yarn.lock"))?"yarn":t(r.join(e,"bun.lockb"))?"bun":t(r.join(e,"package-lock.json"))?"npm":Ba()},Ir=async(e,t,r)=>{if(r.length!==0)try{await Qe(t,[...zi(t),...r],e)}catch(i){throw new Error(`Failed to install packages with ${t}: ${i}`,{cause:i})}},zi=e=>{switch(e){case"npm":return["install"];case"yarn":return["add"];case"pnpm":return["add"];case"bun":return["add"];default:return["install"]}},z=async(e,t)=>{let r=await A(e);await Ir(e,r,[t])},za=e=>{switch(e){case"npm":return"--save-dev";case"yarn":case"pnpm":case"bun":return"-D";default:return"--save-dev"}},fe=async(e,t,r)=>{if(r.length!==0)try{await Qe(t,[...zi(t),za(t),...r],e)}catch(i){throw new Error(`Failed to install dev packages with ${t}: ${i}`,{cause:i})}},At=async(e,t)=>{let r=await A(e);await fe(e,r,[t])},$r=async(e,t,r)=>{if(r.length!==0)try{await Qe(t,[...Va(t),...r],e)}catch(i){throw new Error(`Failed to uninstall packages with ${t}: ${i}`,{cause:i})}},Va=e=>{switch(e){case"npm":return["uninstall"];case"yarn":return["remove"];case"pnpm":return["remove"];case"bun":return["remove"];default:return["uninstall"]}},He=async(e,t)=>{let r=await A(e);await $r(e,r,[t])};import Ja from"enquirer";var Wa=/^[a-z0-9-_]+$/,qa=/^\d+\.\d+\.\d+$/,Vi=e=>Wa.test(e),Xa=e=>qa.test(e),Ji=(e,t={})=>{let r=t.projectName??e??"my-app";if(!Vi(r))throw new Error(`Invalid project name "${r}". Use lowercase letters, digits, hyphens, and underscores only.`);let i=t.version??"0.1.0";if(!Xa(i))throw new Error(`Invalid version "${i}". Expected semver (e.g. 0.1.0).`);let n=t.author??"Teispace",o=t.company??n;return{projectName:r,description:t.description??"A Next.js application",author:n,version:i,packageManager:t.packageManager??"yarn",gitRemote:t.gitRemote??"",pushToRemote:t.pushToRemote??!1,gitIssues:t.gitIssues??"",gitHomepage:t.gitHomepage??"",httpClient:t.httpClient??"fetch",reactSecureStorage:t.reactSecureStorage??!0,email:t.email??"support@example.com",company:o,keepTemplates:t.keepTemplates??!1,darkMode:t.darkMode??!0,redux:t.redux??!0,ws:(t.redux??!0)&&(t.ws??!1),i18n:t.i18n??!0,communityFiles:t.communityFiles??[],readme:t.readme??!0,docker:t.docker??!1,containerName:t.containerName,imageName:t.imageName,imageTag:t.imageTag,ci:t.ci??!1,preCommitHooks:t.preCommitHooks??!0,commitizen:t.commitizen??!0,copyEnv:t.copyEnv??!0,tests:t.tests??!0,reactCompiler:t.reactCompiler??!0,bundleAnalyzer:t.bundleAnalyzer??!1}},{prompt:Ya}=Ja,Wi=async e=>{if(e!==void 0&&!Vi(e))throw new Error(`Invalid project name "${e}". Use lowercase letters, digits, hyphens, and underscores only \u2014 no slashes, "..", or absolute paths.`);let t=await Ya([{type:"input",name:"projectName",message:"What is the project name?",initial:e||"my-app",skip:!!e,validate:r=>/^[a-z0-9-_]+$/.test(r)?!0:"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=>/^\d+\.\d+\.\d+$/.test(r)?!0:"Version must be a valid semantic version (x.y.z)."},{type:"input",name:"email",message:"Support email:",initial:"support@example.com",validate:r=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(r)?!0:"Please enter a valid email address."},{type:"select",name:"packageManager",message:"Which package manager would you like to use?",choices:["npm","yarn","pnpm","bun"],initial:1},{type:"input",name:"gitRemote",message:"GitHub repository URL (optional):",initial:""},{type:"confirm",name:"pushToRemote",message:"Would you like to push the changes to the remote repository?",initial:!0,skip:function(){return!this.state?.answers?.gitRemote}},{type:"input",name:"gitIssues",message:"GitHub issues URL (optional):",initial:"",skip:function(){return!this.state?.answers?.gitRemote},validate:r=>r?/^https:\/\/github\.com\/[\w-]+\/[\w.-]+\/issues$/.test(r)?!0:"Please enter a valid GitHub issues URL (e.g., https://github.com/user/repo/issues)":!0},{type:"input",name:"gitHomepage",message:"GitHub homepage URL (optional):",initial:"",skip:function(){return!this.state?.answers?.gitRemote},validate:r=>r?/^https:\/\/github\.com\/[\w-]+\/[\w.-]+#readme$/.test(r)?!0:"Please enter a valid GitHub homepage URL (e.g., https://github.com/user/repo#readme)":!0},{type:"confirm",name:"keepTemplates",message:"Do you want to keep GitHub issue and pull request templates?",initial:!1},{type:"select",name:"httpClient",message:"Which HTTP client do you want to use?",choices:["axios","fetch","both","none"],initial:1},{type:"confirm",name:"reactSecureStorage",message:"Do you want to include react-secure-storage?",initial:!0,skip:function(){let r=this.state?.answers??this.enquirer?.answers??{};return!!(r.httpClient&&r.httpClient!=="none")}},{type:"confirm",name:"darkMode",message:"Do you want to include Dark Mode (Tailwind + @teispace/next-themes)?",initial:!0},{type:"confirm",name:"redux",message:"Do you want to include Redux Toolkit?",initial:!0},{type:"confirm",name:"ws",message:"Do you want to include WebSocket support (socket.io-client + Redux bridge)?",initial:!1,skip:function(){return!(this.state?.answers??this.enquirer?.answers??{}).redux}},{type:"confirm",name:"i18n",message:"Do you want to include Internationalization (next-intl)?",initial:!0},{type:"confirm",name:"tests",message:"Do you want to include testing setup (Vitest + React Testing Library)?",initial:!0},{type:"confirm",name:"reactCompiler",message:"Do you want to enable the React Compiler?",initial:!0},{type:"confirm",name:"bundleAnalyzer",message:"Do you want to include @next/bundle-analyzer?",initial:!1},{type:"multiselect",name:"communityFiles",message:"Select community files to include:",choices:[{name:"CODE_OF_CONDUCT.md",value:"CODE_OF_CONDUCT.md"},{name:"CONTRIBUTING.md",value:"CONTRIBUTING.md"},{name:"SECURITY.md",value:"SECURITY.md"}],initial:[]},{type:"confirm",name:"readme",message:"Do you want to create a README.md?",initial:!0},{type:"confirm",name:"docker",message:"Do you want to include Docker configuration?",initial:!1},{type:"input",name:"containerName",message:"Docker Container Name:",initial:"next-app",skip:function(){return!(this.state?.answers??this.enquirer?.answers??{}).docker},validate:r=>/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(r)?!0:"Invalid Docker container name."},{type:"input",name:"imageName",message:"Docker Image Name:",initial:"nextjs-starter",skip:function(){return!(this.state?.answers??this.enquirer?.answers??{}).docker},validate:r=>/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(r)?!0:"Invalid Docker image name (must be lowercase)."},{type:"input",name:"imageTag",message:"Docker Image Tag:",initial:"latest",skip:function(){return!(this.state?.answers??this.enquirer?.answers??{}).docker},validate:r=>/^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$/.test(r)?!0:"Invalid Docker image tag."},{type:"confirm",name:"ci",message:"Do you want to include GitHub Actions (CI/CD)?",initial:!1},{type:"confirm",name:"preCommitHooks",message:"Do you want to setup pre-commit hooks (Husky, Commitlint, Lint-staged)?",initial:!0},{type:"confirm",name:"commitizen",message:"Do you want to setup Commitizen?",initial:!0},{type:"confirm",name:"copyEnv",message:"Want to copy .env.example to .env?",initial:!0}]);if(t.company=t.author,t.ws=!!t.redux&&!!t.ws,t.gitRemote&&!t.gitHomepage){let r=t.gitRemote.replace("git@github.com:","https://github.com/").replace(/\.git$/,"");t.gitHomepage=`${r}#readme`,t.gitIssues=`${r}/issues`}return t};w();import Ka from"node:path";var ce=async(e,t)=>{let r=Za(t);await h(Ka.join(e,"test/test-utils.tsx"),r)},Za=({redux:e,i18n:t})=>e&&t?Qa:e?ec:t?tc:rc,Qa=`import { type RenderOptions, render } from '@testing-library/react';
28
- import { NextIntlClientProvider } from 'next-intl';
29
- import type { ReactElement, ReactNode } from 'react';
30
- import { Provider } from 'react-redux';
31
- import { type AppState, type AppStore, makeStore } from '@/store';
32
- import type { SupportedLocale } from '@/types/i18n';
33
-
34
- type TestProvidersProps = {
35
- children: ReactNode;
36
- store?: AppStore;
37
- preloadedState?: Partial<AppState>;
38
- messages?: Record<string, unknown>;
39
- locale?: SupportedLocale;
40
- };
41
-
42
- export function TestProviders({
43
- children,
44
- store,
45
- preloadedState,
46
- messages = {},
47
- locale = 'en',
48
- }: TestProvidersProps) {
49
- const testStore = store ?? makeStore(preloadedState);
50
-
51
- return (
52
- <Provider store={testStore}>
53
- <NextIntlClientProvider locale={locale} messages={messages} timeZone="UTC">
54
- {children}
55
- </NextIntlClientProvider>
56
- </Provider>
57
- );
58
- }
59
-
60
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'> & {
61
- store?: AppStore;
62
- preloadedState?: Partial<AppState>;
63
- messages?: Record<string, unknown>;
64
- locale?: SupportedLocale;
65
- };
66
-
67
- export function renderWithProviders(
68
- ui: ReactElement,
69
- { store, preloadedState, messages, locale, ...renderOptions }: ExtendedRenderOptions = {},
70
- ) {
71
- const testStore = store ?? makeStore(preloadedState);
72
-
73
- return {
74
- store: testStore,
75
- ...render(ui, {
76
- wrapper: ({ children }) => (
77
- <TestProviders store={testStore} messages={messages} locale={locale}>
78
- {children}
79
- </TestProviders>
80
- ),
81
- ...renderOptions,
82
- }),
83
- };
84
- }
85
-
86
- export * from '@testing-library/react';
87
- export { default as userEvent } from '@testing-library/user-event';
88
- `,ec=`import { type RenderOptions, render } from '@testing-library/react';
89
- import type { ReactElement, ReactNode } from 'react';
90
- import { Provider } from 'react-redux';
91
- import { type AppState, type AppStore, makeStore } from '@/store';
92
-
93
- type TestProvidersProps = {
94
- children: ReactNode;
95
- store?: AppStore;
96
- preloadedState?: Partial<AppState>;
97
- };
98
-
99
- export function TestProviders({ children, store, preloadedState }: TestProvidersProps) {
100
- const testStore = store ?? makeStore(preloadedState);
101
-
102
- return <Provider store={testStore}>{children}</Provider>;
103
- }
104
-
105
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'> & {
106
- store?: AppStore;
107
- preloadedState?: Partial<AppState>;
108
- };
109
-
110
- export function renderWithProviders(
111
- ui: ReactElement,
112
- { store, preloadedState, ...renderOptions }: ExtendedRenderOptions = {},
113
- ) {
114
- const testStore = store ?? makeStore(preloadedState);
115
-
116
- return {
117
- store: testStore,
118
- ...render(ui, {
119
- wrapper: ({ children }) => <TestProviders store={testStore}>{children}</TestProviders>,
120
- ...renderOptions,
121
- }),
122
- };
123
- }
124
-
125
- export * from '@testing-library/react';
126
- export { default as userEvent } from '@testing-library/user-event';
127
- `,tc=`import { type RenderOptions, render } from '@testing-library/react';
128
- import { NextIntlClientProvider } from 'next-intl';
129
- import type { ReactElement, ReactNode } from 'react';
130
- import type { SupportedLocale } from '@/types/i18n';
131
-
132
- type TestProvidersProps = {
133
- children: ReactNode;
134
- messages?: Record<string, unknown>;
135
- locale?: SupportedLocale;
136
- };
137
-
138
- export function TestProviders({ children, messages = {}, locale = 'en' }: TestProvidersProps) {
139
- return (
140
- <NextIntlClientProvider locale={locale} messages={messages} timeZone="UTC">
141
- {children}
142
- </NextIntlClientProvider>
143
- );
144
- }
20
+ ${t.description}
145
21
 
146
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'> & {
147
- messages?: Record<string, unknown>;
148
- locale?: SupportedLocale;
149
- };
22
+ Generated from [@teispace/nextjs-starter](https://github.com/teispace/nextjs-starter) ${r.starter.version} with \`@teispace/next-maker\`.
150
23
 
151
- export function renderWithProviders(
152
- ui: ReactElement,
153
- { messages, locale, ...renderOptions }: ExtendedRenderOptions = {},
154
- ) {
155
- return render(ui, {
156
- wrapper: ({ children }) => (
157
- <TestProviders messages={messages} locale={locale}>
158
- {children}
159
- </TestProviders>
160
- ),
161
- ...renderOptions,
162
- });
163
- }
24
+ ## Getting started
164
25
 
165
- export * from '@testing-library/react';
166
- export { default as userEvent } from '@testing-library/user-event';
167
- `,rc=`import { type RenderOptions, render } from '@testing-library/react';
168
- import type { ReactElement, ReactNode } from 'react';
26
+ \`\`\`bash
27
+ ${o} install
28
+ ${ae(o,"dev")}
29
+ \`\`\`
169
30
 
170
- type TestProvidersProps = {
171
- children: ReactNode;
172
- };
31
+ Open [http://localhost:3000](http://localhost:3000). Copy \`.env.example\` to \`.env\` and set \`NEXT_PUBLIC_API_URL\` to your API origin.
173
32
 
174
- export function TestProviders({ children }: TestProvidersProps) {
175
- return <>{children}</>;
176
- }
33
+ ## What is included
177
34
 
178
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'>;
35
+ ${i.map(m=>`- ${m}`).join(`
36
+ `)}
179
37
 
180
- export function renderWithProviders(ui: ReactElement, options: ExtendedRenderOptions = {}) {
181
- return render(ui, {
182
- wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
183
- ...options,
184
- });
185
- }
38
+ ## Scripts
186
39
 
187
- export * from '@testing-library/react';
188
- export { default as userEvent } from '@testing-library/user-event';
189
- `;import Xi from"node:path";var R={REDUX_TOOLKIT:"@reduxjs/toolkit",REACT_REDUX:"react-redux",REDUX:"redux",REDUX_PERSIST:"redux-persist",REACT_SECURE_STORAGE:"react-secure-storage",NEXT_INTL:"next-intl",NEXT_THEMES:"@teispace/next-themes",AXIOS:"axios",HUSKY:"husky",COMMITLINT_CLI:"@commitlint/cli",COMMITLINT_CONFIG:"@commitlint/config-conventional",LINT_STAGED:"lint-staged",COMMITIZEN:"commitizen",CZ_CONVENTIONAL_CHANGELOG:"cz-conventional-changelog",VITEST:"vitest",JSDOM:"jsdom",VITE_PLUGIN_REACT:"@vitejs/plugin-react",TESTING_LIBRARY_DOM:"@testing-library/dom",TESTING_LIBRARY_JEST_DOM:"@testing-library/jest-dom",TESTING_LIBRARY_REACT:"@testing-library/react",TESTING_LIBRARY_USER_EVENT:"@testing-library/user-event",REACT_COMPILER_BABEL:"babel-plugin-react-compiler",NEXT_BUNDLE_ANALYZER:"@next/bundle-analyzer"};P();w();var Yi=async(e,t)=>{if(t.bundleAnalyzer)return;let r=Xi.join(e,a.NEXT_CONFIG);if(l(r)){let i=await g(r);i=i.replace(/import\s+withBundleAnalyzer\s+from\s+'@next\/bundle-analyzer';\n/,""),i=i.replace(/const\s+bundleAnalyzer\s*=\s*withBundleAnalyzer\(\{[\s\S]*?\}\);\n+/,""),i=i.replace(/bundleAnalyzer\(([^)]*)\)/,"$1"),await h(r,i)}await _(Xi.join(e,a.PACKAGE_JSON),i=>(i.devDependencies&&delete i.devDependencies[R.NEXT_BUNDLE_ANALYZER],i.scripts&&delete i.scripts.analyze,i))};P();w();import et from"node:path";var Ki=async(e,t)=>{t.darkMode||(await C(et.join(e,a.THEME_PROVIDER)),await ic(e),await nc(e),await sc(e))},ic=async e=>{let t=et.join(e,a.PROVIDERS_INDEX),r=await g(t);r=r.replace(/export \* from '\.\/CustomThemeProvider';\n/,""),await h(t,r)},nc=async e=>{let t=et.join(e,a.GLOBALS_CSS);if(l(t)){let r=await g(t);r=r.replace(/@custom-variant dark \(.*?\);\n\n/,""),r=r.replace(/@theme \{[\s\S]*?\}\n/,""),await h(t,r)}},oc=e=>e.replace(/\bbg-light\b\s?/g,"").replace(/\bdark:bg-dark\b\s?/g,"").replace(/\s{2,}/g," ").replace(/suppressHydrationWarning=\{true\}/g,""),sc=async e=>{let t=[et.join(e,a.ROOT_LAYOUT),et.join(e,"src/app/[locale]/layout.tsx")];for(let r of t)if(l(r)){let i=await g(r),n=oc(i);n!==i&&await h(r,n)}};P();w();Be();import j from"node:path";var nn=async(e,t)=>{let r=j.join(e,a.HTTP_UTILS),i=t.httpClient!=="none"||!!t.reactSecureStorage;t.httpClient==="none"?(await rt(e),await uc(e,r,i)):t.httpClient==="axios"?(await fc(e,r),await we(e,["axios"]),await Pe(e,["axios"])):t.httpClient==="fetch"?(await gc(e,r),await we(e,["fetch"]),await Pe(e,["fetch"])):t.httpClient},uc=async(e,t,r)=>{await v(j.join(e,a.AXIOS_CLIENT)),await v(j.join(e,a.FETCH_CLIENT)),r?(await h(j.join(t,"index.ts"),`export * from './token-store';
190
- `),await C(j.join(e,a.CLIENT_UTILS)),await Ue(e,[])):(await v(t),await hc(e),await C(j.join(e,a.APP_APIS)),await yc(e)),await xc(e),await v(j.join(e,a.ERRORS_DIR)),r||(await v(j.join(e,a.COMMON_TYPES_DIR)),await v(j.join(e,a.UTILITY_TYPES_DIR)),await wc(e))},fc=async(e,t)=>{await v(j.join(e,a.FETCH_CLIENT));let r=await g(j.join(t,"index.ts"));r=r.replace(/export .* from '\.\/fetch-client';\n/g,""),await h(j.join(t,"index.ts"),r);let i=j.join(e,a.HTTP_TYPES);if(l(i)){let n=await g(i);n=n.replace(/export interface FetchClientOptions \{[\s\S]*?\}\n\n/,""),n=n.replace(/export interface ExtendedRequestInit extends RequestInit \{[\s\S]*?\}\n\n/,""),await h(i,n)}},gc=async(e,t)=>{await v(j.join(e,a.AXIOS_CLIENT));let r=await g(j.join(t,"index.ts"));r=r.replace(/export .* from '\.\/axios-client';\n/g,""),await h(j.join(t,"index.ts"),r),await Ue(e,["fetch"]);let i=j.join(e,a.HTTP_TYPES);if(l(i)){let n=await g(i);n=n.replace(/export interface AxiosClientOptions \{[\s\S]*?\}\n\n/,""),await h(i,n)}},hc=async e=>{let t=j.join(e,a.UTILS_INDEX);if(l(t)){let r=await g(t);r=r.replace(/export \* from '\.\/http';\n/,""),await h(t,r)}},yc=async e=>{let t=j.join(e,a.CONFIG_INDEX);if(l(t)){let r=await g(t);r=r.replace(/export \* from '\.\/app-apis';\n/,""),await h(t,r)}},xc=async e=>{let t=j.join(e,a.CONSTANTS);if(l(t)){let r=await g(t);r=r.replace(/export const API_RESPONSE_DATA_KEY = 'data';\n/,""),r=Or(r),await h(t,r)}},wc=async e=>{let t=j.join(e,a.TYPES_INDEX);if(l(t)){let r=await g(t);r=r.replace(/export \* from '\.\/utility';\n/,""),r=r.replace(/export \* from '\.\/common';\n/,""),await h(t,r)}};P();w();import Q from"node:path";var on=async(e,t)=>{t.i18n||(await vc(e),await Sc(e),await Ec(e),await Tc(e),await Pc(e))},Pc=async e=>{let t=Q.join(e,a.ENV_EXAMPLE);if(!l(t))return;let r=await g(t);r=r.replace(/#[^\n]*IANA time zone[\s\S]*?DEFAULT_TIMEZONE=[^\n]*\n?/,""),r=r.replace(/#[^\n]*Fallback locale[\s\S]*?DEFAULT_LOCALE=[^\n]*\n?/,""),r=r.replace(/^DEFAULT_TIMEZONE=.*\n?/m,""),r=r.replace(/^DEFAULT_LOCALE=.*\n?/m,""),await h(t,r)},vc=async e=>{await v(Q.join(e,a.I18N_DIR)),await v(Q.join(e,a.LOCALE_DIR)),await C(Q.join(e,a.PROXY)),await C(Q.join(e,a.MIDDLEWARE)),await C(Q.join(e,a.I18N_TYPES)),await C(Q.join(e,a.APP_LOCALES))},Sc=async e=>{let t=Q.join(e,a.TYPES_INDEX);if(l(t)){let i=await g(t);i=i.replace(/export \* from '\.\/i18n';\n/,""),await h(t,i)}let r=Q.join(e,a.CONFIG_INDEX);if(l(r)){let i=await g(r);i=i.replace(/export \* from '\.\/app-locales';\n/,""),await h(r,i)}},Ec=async e=>{let t=Q.join(e,a.NEXT_CONFIG);if(l(t)){let r=await g(t);r=r.replace(/import createNextIntlPlugin from 'next-intl\/plugin';\n/,""),r=r.replace(/const withNextIntl = createNextIntlPlugin\(\);\n+/,""),r=r.replace(/withNextIntl\(([^)]+)\)/,"$1"),await h(t,r)}},Tc=async e=>{let t=Q.join(e,a.COUNTER_COMPONENT);if(l(t)){let n=await g(t);n=n.replace(/import\s+\{\s*useTranslations\s*\}\s+from\s+['"]next-intl['"];\n/,""),n=n.replace(/\s*const\s+t\s*=\s*useTranslations\(['"]Count['"]\);\n/,""),n=n.replace(/\{t\('currentCount',\s*\{\s*count:\s*value\s*\}\)\}/g,"Current Count: {value}"),n=n.replace(/\{t\(['"]increment['"]\)\}/g,"Increment"),n=n.replace(/\{t\(['"]decrement['"]\)\}/g,"Decrement"),n=n.replace(/\{t\(['"]reset['"]\)\}/g,"Reset"),await h(t,n)}let r=Q.join(e,"src/features/counter/components/Counter.test.tsx"),i=Q.join(e,"test/test-utils.tsx");await C(r),await C(i)};P();w();import ve from"node:path";var sn=async(e,t)=>{t.httpClient!=="none"||t.reactSecureStorage||await v(ve.join(e,a.STORAGE_SERVICE))},an=async e=>{await C(ve.join(e,a.LICENSE))},cn=async e=>{await C(ve.join(e,a.CHANGELOG))},pn=async e=>{await C(ve.join(e,a.NVM_RC)),await C(ve.join(e,a.NPM_RC))},ln=async e=>{await v(ve.join(e,a.CLAUDE_DIR)),await v(ve.join(e,a.DOCS_DIR)),await v(ve.join(e,a.VSCODE_DIR))};import mn from"node:path";P();w();var dn=async(e,t)=>{if(t.reactCompiler)return;let r=mn.join(e,a.NEXT_CONFIG);if(l(r)){let i=await g(r);i=i.replace(/^\s*reactCompiler:\s*true,?\n/m,""),await h(r,i)}await _(mn.join(e,a.PACKAGE_JSON),i=>(i.devDependencies&&delete i.devDependencies[R.REACT_COMPILER_BABEL],i))};P();w();import $e from"node:path";var un=async(e,t)=>{t.redux||(await v($e.join(e,a.STORE)),await v($e.join(e,a.COUNTER_FEATURE)),await C($e.join(e,a.STORE_PROVIDER)),await C($e.join(e,"test/test-utils.tsx")),await Cc(e),await Rc(e))},Cc=async e=>{let t=$e.join(e,a.PROVIDERS_INDEX),r=await g(t);r=r.replace(/export \* from '\.\/StoreProvider';\n/,""),await h(t,r)},Rc=async e=>{let t=[$e.join(e,a.ROOT_PAGE),$e.join(e,a.LOCALE_PAGE)];for(let r of t)if(l(r)){let i=await g(r);i=i.replace(/import\s+\{\s*Counter\s*\}\s+from\s+['"]@\/features\/counter['"];\n?/,""),i=i.replace(/<Counter\s*\/>\n?/g,""),await h(r,i)}};P();w();import fn from"node:path";var gn=async(e,t)=>{if(t.i18n)return;let r=fn.join(e,a.SEO_FILE);l(r)&&await h(r,bc);let i=fn.join(e,a.SITEMAP_FILE);l(i)&&await h(i,Ic)},bc=`import type { Metadata } from 'next';
40
+ | Script | Purpose |
41
+ | :-- | :-- |
42
+ ${a.map(([m,c])=>`| \`${ae(o,m)}\` | ${c} |`).join(`
43
+ `)}
191
44
 
192
- import { env } from '@/lib/env';
45
+ ## Guides
193
46
 
194
- const APP_URL = env.NEXT_PUBLIC_APP_URL;
195
- const APP_NAME = 'Nextjs Starter';
196
- /** Route served by \`src/app/opengraph-image.tsx\`; relative so \`metadataBase\` resolves it. */
197
- const DEFAULT_OG_IMAGE_PATH = '/opengraph-image';
47
+ - \`AGENTS.md\`: conventions and stack decisions (also read by coding agents)
48
+ - \`docs/data-layer.md\`: how reads, mutations, caching, and sessions fit together
49
+ - \`src/features/README.md\`: how to add a feature
50
+ - \`src/lib/http/README.md\`: the HTTP client
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
198
52
 
199
- type SEOParams = {
200
- title: string;
201
- description: string;
202
- path?: string;
203
- image?: string;
204
- noIndex?: boolean;
205
- };
53
+ Generators: \`npx @teispace/next-maker feature <name>\`, \`page <name>\`, \`component <name>\`, \`env <NAME>\`${n.i18n?", `locale <code>`":""}.
54
+ `;await k(s,p)};var xe=async e=>{await kt(e.projectPath,{source:e.source});let t=await He(e.projectPath);return tt(t,e.supplied,e.packageManager)},tt=(e,t,r)=>{let{answers:n,forced:o,unknown:s}=Ke(e,t),i=n.packageManager??r??"pnpm";return{manifest:e,answers:n,forced:o,unknown:s,packageManager:i,plan:Xt(e,n,i)}},be=async(e,t,r,n)=>{let{manifest:o,answers:s,packageManager:i,plan:a}=r,p=await Qt(e,o,a);return await xn(e,t),await Rn(e,t),await jn(e,t),await Cn(e,t,o,s,i),await me(e,{cli:Te.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}}),p};var nr=["pnpm","npm","yarn","bun"],Gi=e=>{if(e!==void 0){if(!nr.includes(e))throw new Error(`Invalid --package-manager "${e}". Valid: ${nr.join(", ")}.`);return e}},Bi=(e,t=[])=>[...t,e],Tn=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 (${Ie().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",Bi).option("--package-manager <pm>",`Package manager (${nr.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 zi(t,r)}catch(n){x(`${n instanceof Error?n.message:n}`),process.exit(1)}})},rt=async(e,t,r,n)=>{let o=$t(e);n.current=o;try{let s=await r();return o.succeed(t),s}finally{n.current===o&&(n.current=null)}},Vi=e=>Array.isArray(e)?e.length?e.join(", "):"none":String(e),qi=(e,t,r)=>{l(A.bold("Options"));for(let[n,o]of Object.entries(t.answers)){let s=e.options[n]?.label??n;l(` ${A.dim(s.padEnd(44))} ${Vi(o)}`)}for(let n of r)l(A.yellow(` ! ${n.reason}; ${n.option} turned off`));l(""),l(A.bold("Composition")),l(` ${A.green("keep")} ${t.featuresOn.join(", ")||"(none)"}`),l(` ${A.red("drop")} ${t.featuresOff.join(", ")||"(none)"}`),(t.featureOverlays.length||t.pmOverlays.length)&&l(` ${A.cyan("overlay")} ${[...t.pmOverlays,...t.featureOverlays].join(", ")}`),(t.packages.length||t.devPackages.length)&&l(` ${A.red("uninstall")} ${[...t.packages,...t.devPackages].join(", ")}`),l("")},Ji=async(e,t)=>{let r=t.config?await Zt(t.config):void 0,n=Gi(t.packageManager??r?.packageManager),s={...t.preset?Ne(t.preset):r?.preset?Ne(r.preset):{},...r?.options??{},...ye(t.set)};n&&(s.packageManager=n);let i=!(t.yes||r);return{identity:i?await wn(e):le(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}},zi=async(e,t)=>{vt();let r=await Ji(e,t),{identity:n}=r,o=Ct.resolve(process.cwd(),n.projectName);if(y(o))throw new Error(`Directory ${n.projectName} already exists.`);let s={current:null},i=async()=>{y(o)&&(s.current?.stop(),s.current=null,l(A.yellow(`
55
+ Cleaning up ${n.projectName}...`)),await Ze(o))},a=xt(async()=>{l(A.red(`
56
+ Process interrupted.`)),await i()});try{let p=B({starterPath:t.starterPath}),m=await rt(`Fetching starter (${p.location})...`,"Starter fetched.",()=>xe({projectPath:o,source:p,supplied:r.suppliedOptions,packageManager:r.packageManager}),s);if(l(A.dim(` ${m.manifest.starter.name} ${m.manifest.starter.version}`)),l(""),r.interactive){let f={...r.suppliedOptions,...await Rt(m.manifest,m.answers)};t.packageManager&&(f.packageManager=t.packageManager),m=tt(m.manifest,f,r.packageManager),l("")}for(let f of m.unknown)l(A.yellow(` ! unknown option "${f}" ignored`));let{packageManager:c,plan:d,manifest:u}=m;if(qi(u,d,m.forced),t.dryRun){a(),await Ze(o),l(A.cyan("Dry run: nothing was created."));return}await rt("Composing project...","Project composed.",()=>be(o,n,m,p),s),r.install&&(await rt(`Installing dependencies with ${c}...`,"Dependencies installed.",()=>pe(o,c),s),await rt("Formatting...","Formatted.",()=>Et(o,c,"lint:fix"),s)),n.copyEnv&&y(Ct.join(o,".env.example"))&&await Ui(Ct.join(o,".env.example"),Ct.join(o,".env")),r.git&&await rt("Initializing git...","Git initialized.",()=>St(o,n.gitRemote,n.pushToRemote),s),a(),l(""),l(A.green(`\u2728 ${n.projectName} is ready.`)),l(""),l("Next:"),l(A.cyan(` cd ${n.projectName}`)),r.install||l(A.cyan(` ${c} install`)),l(A.cyan(` ${c==="pnpm"||c==="yarn"?`${c} dev`:`${c} run dev`}`)),l("")}catch(p){throw a(),s.current?.fail("Failed to create project."),s.current=null,await i(),p}};import Cr from"node:path";import K from"picocolors";import Tt from"node:path";var On=/^[A-Za-z][A-Za-z0-9-]*$/,Wi=/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;function An(e){return Wi.test(e)}function F(e,t="name"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);if(!On.test(r))throw new Error(`Invalid ${t} "${e}". Use letters, digits, and hyphens only \u2014 no slashes, spaces, "..", or absolute paths.`);if(An(r))throw new Error(`Invalid ${t} "${e}". That is a reserved name on Windows.`);return r}function W(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(!On.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(An(o))throw new Error(`Invalid ${t} "${e}". Segment "${o}" is reserved on Windows.`)}return n}function te(e,...t){let r=Tt.resolve(e),n=Tt.resolve(r,...t),o=Tt.relative(r,n);if(o===""||o===".")return n;if(o.startsWith("..")||Tt.isAbsolute(o))throw new Error(`Refusing to write outside the project directory (resolved path escapes "${r}").`);return n}import{access as Hi}from"node:fs/promises";import Ki from"node:path";var ve=async(e,t,r)=>{try{return await Hi(Ki.join(e,r,t)),!0}catch{return!1}};import{access as Xi,readFile as Nn}from"node:fs/promises";import or from"node:path";var _e=async e=>{try{return await Xi(e),!0}catch{return!1}},In=async e=>{try{return JSON.parse(await Nn(e,"utf-8"))}catch{return null}},T=async e=>{let t=await In(or.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 In(or.join(e,Fe)),o=(...w)=>or.join(e,...w),s=await _e(o("src","store")),i=!!(r["@reduxjs/toolkit"]&&r["react-redux"])&&s,a=!!r.zustand&&s&&!i,p=i?"redux":a?"zustand":"none",m=await _e(o("src","lib","http","core","client.ts")),c=!!r.axios&&await _e(o("src","lib","http","adapters","axios.ts")),d="none";if(m)if(!c)d="fetch";else{let w="";try{w=await Nn(o("src","lib","http","client.ts"),"utf-8")}catch{}d=w.includes("axiosAdapter")?"axios":"both"}let u=!!r["next-intl"]&&await _e(o("src","i18n")),f=!!r.vitest||await _e(o("vitest.config.ts")),h=r["socket.io-client"]&&await _e(o("src","lib","ws"))?"present":"none";return{state:p,hasRedux:i,httpClient:d,hasI18n:u,hasTests:f,ws:h,hasQuery:!!r["@tanstack/react-query"],hasActions:!!r["next-safe-action"],record:n??void 0}};import{mkdir as Yi,writeFile as Qi}from"node:fs/promises";import sr from"node:path";var Fn=({pascalName:e,camelName:t})=>`import { z } from 'zod';
206
57
 
207
- export function generateSEOMetadata({
208
- title,
209
- description,
210
- path = '',
211
- image,
212
- noIndex = false,
213
- }: SEOParams): Metadata {
214
- const url = \`\${APP_URL}\${path}\`;
215
- const ogImage = image ?? DEFAULT_OG_IMAGE_PATH;
216
-
217
- return {
218
- title,
219
- description,
220
- metadataBase: new URL(APP_URL),
221
- alternates: {
222
- canonical: url,
223
- },
224
- openGraph: {
225
- type: 'website',
226
- title,
227
- description,
228
- url,
229
- siteName: APP_NAME,
230
- images: [
231
- {
232
- url: ogImage,
233
- width: 1200,
234
- height: 630,
235
- alt: title,
236
- },
237
- ],
238
- locale: 'en_US',
239
- },
240
- twitter: {
241
- card: 'summary_large_image',
242
- title,
243
- description,
244
- images: [ogImage],
245
- },
246
- robots: noIndex
247
- ? { index: false, follow: false }
248
- : {
249
- index: true,
250
- follow: true,
251
- 'max-video-preview': -1,
252
- 'max-image-preview': 'large' as const,
253
- 'max-snippet': -1,
254
- },
255
- };
256
- }
58
+ /**
59
+ * Wire contracts for the ${t} feature. Types are inferred from the
60
+ * schemas, so a response that drifts from the contract fails validation at
61
+ * the transport instead of surfacing as \`undefined\` in a component.
62
+ */
63
+ export const ${t}Schema = z.object({
64
+ id: z.string(),
65
+ title: z.string(),
66
+ createdAt: z.string(),
67
+ });
68
+ export type ${e} = z.infer<typeof ${t}Schema>;
257
69
 
258
- export { APP_NAME, APP_URL, DEFAULT_OG_IMAGE_PATH };
259
- `,Ic=`import type { MetadataRoute } from 'next';
70
+ export const ${t}ListSchema = z.array(${t}Schema);
260
71
 
261
- import { env } from '@/lib/env';
72
+ export const create${e}InputSchema = z.object({
73
+ title: z.string().min(1).max(200),
74
+ });
75
+ export type Create${e}Input = z.infer<typeof create${e}InputSchema>;
262
76
 
263
- const APP_URL = env.NEXT_PUBLIC_APP_URL;
77
+ export const update${e}InputSchema = create${e}InputSchema.partial().extend({
78
+ id: z.string(),
79
+ });
80
+ export type Update${e}Input = z.infer<typeof update${e}InputSchema>;
81
+ `,Mn=({kebabName:e,camelName:t})=>`/**
82
+ * Query keys owned by the feature. Everything under \`${t}Keys.all\` can
83
+ * be invalidated at once; narrower keys target one resource.
84
+ */
85
+ export const ${t}Keys = {
86
+ all: ['${e}'] as const,
87
+ list: () => [...${t}Keys.all, 'list'] as const,
88
+ detail: (id: string) => [...${t}Keys.all, 'detail', id] as const,
89
+ } as const;
90
+ `,_n=({pascalName:e,camelName:t})=>`import 'server-only';
264
91
 
265
- export default function sitemap(): MetadataRoute.Sitemap {
266
- const paths = ['/'];
92
+ import { AppApis } from '@/lib/config/app-apis';
93
+ import { serverHttp } from '@/lib/http/server';
267
94
 
268
- return paths.map((path) => ({
269
- url: \`\${APP_URL}\${path}\`,
270
- lastModified: new Date(),
271
- changeFrequency: 'monthly' as const,
272
- priority: path === '/' ? 1 : 0.8,
273
- }));
274
- }
275
- `;import $c from"node:fs/promises";import it from"node:path";P();w();var kc=[R.VITEST,R.JSDOM,R.VITE_PLUGIN_REACT,R.TESTING_LIBRARY_DOM,R.TESTING_LIBRARY_JEST_DOM,R.TESTING_LIBRARY_REACT,R.TESTING_LIBRARY_USER_EVENT],hn=async e=>{let t;try{t=await $c.readdir(e,{withFileTypes:!0})}catch{return}for(let r of t){let i=it.join(e,r.name);if(r.isDirectory()){if(r.name==="__test-utils__"||r.name==="__tests__"){await v(i);continue}await hn(i)}else r.isFile()&&/\.(test|spec)\.tsx?$/.test(r.name)&&await C(i)}},yn=async(e,t)=>{t.tests||(await C(it.join(e,a.VITEST_CONFIG)),await v(it.join(e,a.TEST_DIR)),await hn(it.join(e,a.SRC)),await _(it.join(e,a.PACKAGE_JSON),r=>{if(r.devDependencies)for(let i of kc)delete r.devDependencies[i];return r.scripts&&(delete r.scripts.test,delete r.scripts["test:watch"],delete r.scripts["test:coverage"],typeof r.scripts.validate=="string"&&(r.scripts.validate=r.scripts.validate.replace(/\s*&&\s*yarn\s+test(?![:\w])/,"").replace(/\byarn\s+test(?![:\w])\s*&&\s*/,""))),r}))};P();w();import ze from"node:path";P();w();import xn from"node:path";import{readFile as Nc,writeFile as Ac}from"node:fs/promises";import _c from"node:path";var nt=(e,t)=>{let r=/import\s+.*\s+from\s+['"].*['"];?\n/g,i=e.match(r);if(i&&i.length>0){let n=i[i.length-1],o=e.lastIndexOf(n);return e.slice(0,o+n.length)+t+`
276
- `+e.slice(o+n.length)}return`${t}
277
- ${e}`},Mr=(e,t,r)=>{let i=/combineReducers\(\{([^}]*)\}\)/s,n=e.match(i);if(!n)throw new Error("Could not find combineReducers in rootReducer.ts");let o=n[1],s=`
278
- ${t}: ${r},`,c=o.trimEnd()+s;return e.replace(i,`combineReducers({${c}
279
- })`)},ot=(e,t)=>{let r=e.lastIndexOf("} as const;");if(r===-1)throw new Error("Could not find closing brace of AppApis object");let i=e.substring(0,r),n=e.substring(r);return`${i.trimEnd()}
280
- ${t}
281
- ${n}`};var ke=async e=>{let{projectPath:t,name:r,persist:i,importPath:n}=e,o=_c.join(t,"src","store","rootReducer.ts");try{let s=await Nc(o,"utf-8"),c=G(r),p=`${c}Reducer`,u=`${c}PersistConfig`,d=n.replace(/^src\//,"@/"),f=i?`import { ${p}, ${u} } from '${d}';`:`import { ${p} } from '${d}';`;s=nt(s,f);let x=i?`persistReducer(${u}, ${p})`:p;s=Mr(s,c,x),await Ac(o,s)}catch(s){throw new Error(`Failed to register reducer '${r}' in rootReducer: ${s}`,{cause:s})}};var wn="import { attachWsBridge, wsClient } from '@/lib/utils/ws';",Fc=`
282
- // Bridge the WS client's lifecycle into the Redux slice exactly once per
283
- // store instance. The bridge does not open a connection \u2014 that happens
284
- // lazily on first \`useWsEvent\` subscription or an explicit \`connect()\`.
285
- // Effect runs in the browser only, so the SSR boundary is safe.
286
- useEffect(() => {
287
- const store = storeRef.current;
288
- if (!store) return;
289
- return attachWsBridge(wsClient, store.dispatch);
290
- }, []);
291
- `,Ft=async e=>{await ke({projectPath:e,name:"ws",persist:!1,importPath:"src/store/slices/ws.slice"})},Oc=e=>{if(e.includes("from '@/lib/utils/ws'"))return e;let t=e,r=/(^import\s[^\n]*\n)+/m,i=t.match(r);if(!i)throw new Error("injectBridgeMount: no `import` lines found \u2014 expected a StoreProvider with imports.");let n=(i.index??0)+i[0].length;t=`${t.slice(0,n)}${wn}
292
- ${t.slice(n)}`,t=Mc(t);let o=/(if\s*\(\s*!storeRef\.current\s*\)\s*\{[\s\S]*?\n\s*\})/;if(!t.match(o))throw new Error("injectBridgeMount: could not locate `if (!storeRef.current) { ... }` initializer \u2014 StoreProvider shape unexpected.");return t=t.replace(o,`$1
293
- ${Fc.trimEnd()}
294
- `),t},Ot=e=>{let t=e;return t=t.replace(new RegExp(`${Gc(wn)}\\n?`),""),t=t.replace(/\n(?:[ \t]*\/\/[^\n]*\n)*[ \t]*useEffect\(\(\)\s*=>\s*\{[\s\S]*?attachWsBridge\(wsClient,\s*store\.dispatch\)[\s\S]*?\},\s*\[\]\);\n?/,`
295
- `),/\buseEffect\s*\(/.test(t)||(t=Dc(t)),t},Dc=e=>{let t=/import\s*\{([^}]*)\}\s*from\s*['"]react['"];\n?/,r=e.match(t);if(!r)return e;let i=r[1].split(",").map(o=>o.trim()).filter(o=>o.length>0&&o!=="useEffect");if(i.length===0)return e.replace(t,"");let n=`import { ${i.join(", ")} } from 'react';
296
- `;return e.replace(t,n)},Dt=async e=>{let t=xn.join(e,a.STORE_PROVIDER);if(!l(t))return;let r=await g(t),i=Oc(r);i!==r&&await h(t,i)};var jt=async e=>{let t=xn.join(e,a.TEST_SETUP_FILE);if(!l(t))return;let r=await g(t),i=!1;r.includes("vi.mock('react-secure-storage'")||(r+=jc,i=!0),r.includes("vi.mock('server-only'")||(r+=Lc,i=!0),i&&await h(t,r)},jc=`
297
- // \`react-secure-storage\` constructs an \`EncryptionService\` at module load
298
- // that fingerprints the browser via \`<canvas>.getContext\`. jsdom doesn't
299
- // implement canvas, so the import explodes anywhere it's pulled in
300
- // transitively (HTTP token store, WS auth carrier, etc.). Stub it globally \u2014
301
- // tests that need real behaviour are integration tests, not unit tests.
302
- vi.mock('react-secure-storage', () => {
303
- const store = new Map<string, unknown>();
304
- return {
305
- default: {
306
- setItem: (k: string, v: unknown) => store.set(k, v),
307
- getItem: (k: string) => store.get(k) ?? null,
308
- removeItem: (k: string) => store.delete(k),
309
- clear: () => store.clear(),
310
- },
311
- };
312
- });
313
- `,Lc=`
314
- // \`server-only\` throws at module load outside a Next.js server bundle \u2014
315
- // the package relies on bundler-level redirect to a no-op file in server
316
- // environments. Vitest doesn't do that redirect, so we stub it as a no-op
317
- // to let unit tests reach the server-side modules. Production builds are
318
- // unaffected; Next.js still routes \`server-only\` to its real implementation.
319
- vi.mock('server-only', () => ({}));
320
- `,Mc=e=>{let t=/import\s*\{([^}]*)\}\s*from\s*['"]react['"];/,r=e.match(t);if(!r)return e;let i=r[1];if(/\buseEffect\b/.test(i))return e;let n=i.split(",").map(s=>s.trim()).filter(Boolean);n.push("useEffect"),n.sort();let o=`import { ${n.join(", ")} } from 'react';`;return e.replace(t,o)},Gc=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),Lt=e=>{let t=e;return t=t.replace(/^import\s*\{\s*wsReducer\s*\}\s*from\s*['"][^'"]*ws\.slice['"];\n?/m,""),t=t.replace(/^\s*ws:\s*wsReducer,?\n/m,""),t=t.replace(/\/\*\*[^*]*(?:\*(?!\/)[^*]*)*\bws\b[^*]*(?:\*(?!\/)[^*]*)*\bpersistReducer\b[\s\S]*?\*\/\n?/,""),t};var Pn=async(e,t)=>{t.ws||(await v(ze.join(e,a.WS_UTILS)),await C(ze.join(e,a.WS_SLICE_FILE)),await C(ze.join(e,a.WS_SLICE_TEST_FILE)),t.redux&&(await Hc(e),await Uc(e)),await Bc(e))},Hc=async e=>{let t=ze.join(e,a.ROOT_REDUCER);if(!l(t))return;let r=await g(t),i=Lt(r);i!==r&&await h(t,i)},Uc=async e=>{let t=ze.join(e,a.STORE_PROVIDER);if(!l(t))return;let r=await g(t),i=Ot(r);i!==r&&await h(t,i)},Bc=async e=>{let t=ze.join(e,a.PACKAGE_JSON);if(!l(t))return;let r=JSON.parse(await g(t));({...r.dependencies,...r.devDependencies})["socket.io-client"]&&await He(e,"socket.io-client")};var vn=async(e,t)=>{let r=S("Customizing features...");try{await nn(e,t),await sn(e,t),await Pn(e,t),await un(e,t),await Ki(e,t),await Yi(e,t),await on(e,t),await gn(e,t),await dn(e,t),await yn(e,t),await an(e),await cn(e),await pn(e),await ln(e),t.tests&&await ce(e,{redux:t.redux,i18n:t.i18n}),r.succeed("Features customized.")}catch(i){throw r.fail("Failed to customize features."),i}};import zc from"node:path";P();w();var Sn=async(e,t)=>{let r=S("Configuring package.json...");try{let i=t.gitRemote,n=t.gitHomepage,o=t.gitIssues;t.gitRemote&&(t.gitRemote.startsWith("git@github.com:")&&(i=t.gitRemote.replace("git@github.com:","https://github.com/").replace(/\.git$/,"")),n||(n=`${i.replace(/\.git$/,"")}#readme`),o||(o=`${i.replace(/\.git$/,"")}/issues`),i.endsWith(".git")||(i=`${i}.git`)),await _(zc.join(e,a.PACKAGE_JSON),s=>(s.name=t.projectName,s.version=t.version,s.description=t.description,s.author=t.author,delete s.packageManager,t.gitRemote?(n&&(s.homepage=n),o&&(s.bugs={url:o}),i&&(s.repository={type:"git",url:i})):(delete s.homepage,delete s.bugs,delete s.repository),t.redux||(delete s.dependencies[R.REDUX_TOOLKIT],delete s.dependencies[R.REACT_REDUX],delete s.dependencies[R.REDUX],delete s.dependencies[R.REDUX_PERSIST]),t.httpClient!=="none"||t.reactSecureStorage||delete s.dependencies[R.REACT_SECURE_STORAGE],t.i18n||delete s.dependencies[R.NEXT_INTL],t.darkMode||delete s.dependencies[R.NEXT_THEMES],t.httpClient==="none"?delete s.dependencies[R.AXIOS]:t.httpClient==="fetch"&&delete s.dependencies[R.AXIOS],s)),r.succeed("package.json configured.")}catch(i){throw r.fail("Failed to configure package.json."),i}};import{readdir as En}from"node:fs/promises";import L from"node:path";P();w();var Tn=async(e,t)=>{await Vc(e,t),await Jc(e,t),await Wc(e,t),await qc(e,t),await Xc(e,t),await Yc(e,t),await Kc(e,t)},Vc=async(e,t)=>{t.preCommitHooks||(await v(L.join(e,a.HUSKY_DIR)),await C(L.join(e,a.COMMITLINT_CONFIG)),await C(L.join(e,a.LINTSTAGED_RC)),await _(L.join(e,a.PACKAGE_JSON),r=>(delete r.devDependencies[R.HUSKY],delete r.devDependencies[R.COMMITLINT_CLI],delete r.devDependencies[R.COMMITLINT_CONFIG],delete r.devDependencies[R.LINT_STAGED],delete r.scripts.prepare,delete r.scripts.postinstall,delete r.commitlint,delete r["lint-staged"],r)))},Jc=async(e,t)=>{t.commitizen||(await C(L.join(e,a.CZRC)),await _(L.join(e,a.PACKAGE_JSON),r=>(delete r.devDependencies[R.COMMITIZEN],delete r.devDependencies[R.CZ_CONVENTIONAL_CHANGELOG],delete r.config?.commitizen,r.config&&Object.keys(r.config).length===0&&delete r.config,delete r.scripts.commit,r)))},Wc=async(e,t)=>{t.ci||await v(L.join(e,a.GITHUB_WORKFLOWS))},qc=async(e,t)=>{let r=L.join(e,a.GITHUB_DIR);if(!t.keepTemplates)await v(L.join(r,a.GITHUB_ISSUE_TEMPLATE)),await C(L.join(r,a.GITHUB_PR_TEMPLATE));else{let i=L.join(r,a.GITHUB_ISSUE_TEMPLATE),n=L.join(r,a.GITHUB_PR_TEMPLATE),o=s=>s.replace(/Teispace/g,t.company).replace(/support@teispace\.com/g,t.email).replace(/Next\.js Starter/g,t.projectName).replace(/\[AUTHOR\]/g,t.author).replace(/\[COMPANY\]/g,t.company).replace(/\[EMAIL\]/g,t.email);if(l(n)){let s=await g(n);s=o(s),await h(n,s)}try{if(l(i)){let s=await En(i);for(let c of s){let p=L.join(i,c),u=await g(p);u=o(u),await h(p,u)}}}catch{}}},Xc=async(e,t)=>{let r=[a.CODE_OF_CONDUCT,a.CONTRIBUTING,a.SECURITY];for(let i of r)t.communityFiles.includes(i)||await C(L.join(e,i))},Yc=async(e,t)=>{if(t.docker){let r=L.join(e,a.ENV_EXAMPLE);if(l(r)){let i=await g(r),n=(o,s)=>{let c=new RegExp(`${o}=.*`);c.test(i)?i=i.replace(c,`${o}=${s}`):i+=`${o}=${s}
321
- `};n("CONTAINER_NAME",t.containerName||"next-app"),n("IMAGE_NAME",t.imageName||"nextjs-starter"),n("IMAGE_TAG",t.imageTag||"latest"),await h(r,i)}}else{await C(L.join(e,a.DOCKERFILE)),await C(L.join(e,a.DOCKER_COMPOSE)),await C(L.join(e,a.DOCKERIGNORE));let r=L.join(e,a.ENV_EXAMPLE);if(l(r)){let i=await g(r);i=i.replace(/# Docker Compose Configuration\n/,""),i=i.replace(/CONTAINER_NAME=.*\n/,""),i=i.replace(/IMAGE_NAME=.*\n/,""),i=i.replace(/IMAGE_TAG=.*\n/,""),await h(r,i)}}},Kc=async(e,t)=>{let r=async o=>{let s=await En(o,{withFileTypes:!0}),c=[];for(let p of s){let u=L.join(o,p.name);p.isDirectory()&&p.name!=="node_modules"&&p.name!==".git"?c.push(...await r(u)):p.isFile()&&p.name.toLowerCase()==="readme.md"&&c.push(u)}return c},i=await r(e),n=L.join(e,a.README);for(let o of i)o!==n&&await C(o);if(t.readme){let o=`# ${t.projectName}
95
+ import { ${t}ListSchema, ${t}Schema } from './schema';
322
96
 
323
- ${t.description}
97
+ export const ${t.toUpperCase()}_TAG = '${t}';
324
98
 
325
- ## Getting Started
99
+ /**
100
+ * Data access for Server Components. These reads act as the signed-in user
101
+ * (\`serverHttp\` forwards the session cookie), so they must not be wrapped in
102
+ * \`use cache\` and the calling component must sit under \`<Suspense>\`.
103
+ *
104
+ * For public, user-independent data use \`publicServerHttp\` inside a
105
+ * \`'use cache'\` function with \`cacheTag(${t.toUpperCase()}_TAG)\` and a \`cacheLife\`.
106
+ */
107
+ export const list${e}s = async () =>
108
+ serverHttp.get(AppApis.${t}.list, { schema: ${t}ListSchema });
326
109
 
327
- First, run the development server:
110
+ export const get${e} = async (id: string) =>
111
+ serverHttp.get(AppApis.${t}.detail(id), { schema: ${t}Schema });
112
+ `,Dn=({pascalName:e,camelName:t})=>`'use server';
328
113
 
329
- \`\`\`bash
330
- ${t.packageManager==="npm"?"npm run dev":`${t.packageManager} dev`}
331
- \`\`\`
114
+ import { revalidateTag } from 'next/cache';
332
115
 
333
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
334
- `;await h(n,o)}else await C(n)};P();w();import Gr from"node:path";var Cn=async(e,t)=>{let r=[];t.i18n&&r.push("import { type AbstractIntlMessages, NextIntlClientProvider } from 'next-intl';"),t.darkMode&&r.push("import { CustomThemeProvider } from '@/providers';"),t.redux&&r.push("import type { AppState } from '@/store';"),t.i18n&&r.push("import type { SupportedLocale } from '@/types/i18n';"),t.redux&&r.push("import { StoreProvider } from './StoreProvider';");let i=["children: React.ReactNode;"],n=["children"];t.i18n&&(i.push("locale: SupportedLocale;"),i.push("messages: AbstractIntlMessages;"),i.push("timeZone: string;"),n.push("locale","messages","timeZone")),t.redux&&(i.push("preloadedState?: Partial<AppState>;"),n.push("preloadedState"));let o="{children}";t.i18n&&(o=`<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
335
- ${o}
336
- </NextIntlClientProvider>`),t.darkMode&&(o=`<CustomThemeProvider>
337
- ${o}
338
- </CustomThemeProvider>`),t.redux&&(o=`<StoreProvider preloadedState={preloadedState}>
339
- ${o}
340
- </StoreProvider>`),o==="{children}"&&(o="<>{children}</>");let s=`type RootProviderProps = {
341
- ${i.join(`
342
- `)}
343
- };`,c=`'use client';
344
- ${r.join(`
345
- `)}
116
+ import { authActionClient } from '@/lib/actions';
117
+ import { AppApis } from '@/lib/config/app-apis';
118
+ import { serverHttp } from '@/lib/http/server';
346
119
 
347
- ${s}
120
+ import { create${e}InputSchema, ${t}Schema, update${e}InputSchema } from './schema';
121
+ import { ${t.toUpperCase()}_TAG } from './server';
348
122
 
349
- export const RootProvider = ({ ${n.join(", ")} }: RootProviderProps) => {
350
- return (
351
- ${o}
352
- );
353
- };
354
- `;await h(Gr.join(e,a.ROOT_PROVIDER),c)},Rn=async(e,t)=>{if(t.i18n)return;let r=`\`\${livvic.variable} ${t.darkMode?"bg-light antialiased dark:bg-dark":"antialiased"}\``,i=(t.redux,""),n=t.httpClient!=="none",o=n?`// Regression sentinel \u2014 see file comment for what this guards.
355
- import { HttpClientBundleSentinel } from '@/lib/utils/http/__bundle-sentinel__/client-bundle-sentinel';
356
- `:"",s=n?`
357
- <HttpClientBundleSentinel />`:"",c=`import type { Metadata } from 'next';
358
- import '@/styles/globals.css';
359
- import { Livvic } from 'next/font/google';
360
- ${o}import { RootProvider } from '@/providers';
361
-
362
- const livvic = Livvic({
363
- subsets: ['latin'],
364
- variable: '--font-livvic',
365
- weight: ['100', '200', '300', '400', '500', '600', '700', '900'],
366
- display: 'swap',
367
- });
123
+ /**
124
+ * Server Actions. \`authActionClient\` validates the input, loads the session
125
+ * (\`ctx.user\`), refuses anonymous callers, and turns thrown errors into a
126
+ * plain \`result.serverError\`.
127
+ */
128
+ export const create${e} = authActionClient
129
+ .metadata({ name: '${t}.create' })
130
+ .inputSchema(create${e}InputSchema)
131
+ .action(async ({ parsedInput }) => {
132
+ const result = await serverHttp.post(AppApis.${t}.create, parsedInput, {
133
+ schema: ${t}Schema,
134
+ });
135
+ if (!result.ok) throw result.error;
136
+ revalidateTag(${t.toUpperCase()}_TAG, 'max');
137
+ return result.data;
138
+ });
368
139
 
369
- export const metadata: Metadata = {
370
- title: '${t.projectName}',
371
- description: '${t.description}',
372
- };
140
+ export const update${e} = authActionClient
141
+ .metadata({ name: '${t}.update' })
142
+ .inputSchema(update${e}InputSchema)
143
+ .action(async ({ parsedInput: { id, ...changes } }) => {
144
+ const result = await serverHttp.patch(AppApis.${t}.update(id), changes, {
145
+ schema: ${t}Schema,
146
+ });
147
+ if (!result.ok) throw result.error;
148
+ revalidateTag(${t.toUpperCase()}_TAG, 'max');
149
+ return result.data;
150
+ });
373
151
 
374
- export default function RootLayout({
375
- children,
376
- }: Readonly<{
377
- children: React.ReactNode;
378
- }>) {
379
- return (
380
- <html lang="en"${t.darkMode?" suppressHydrationWarning={true}":""}>
381
- <body className={${r}}>
382
- <RootProvider${i}>${s}
383
- {children}
384
- </RootProvider>
385
- </body>
386
- </html>
387
- );
388
- }
389
- `;await h(Gr.join(e,a.ROOT_LAYOUT),c);let p=t.redux?`import { Counter } from '@/features/counter';
152
+ export const delete${e} = authActionClient
153
+ .metadata({ name: '${t}.delete' })
154
+ .inputSchema(${t}Schema.pick({ id: true }))
155
+ .action(async ({ parsedInput }) => {
156
+ const result = await serverHttp.delete<void>(AppApis.${t}.delete(parsedInput.id));
157
+ if (!result.ok) throw result.error;
158
+ revalidateTag(${t.toUpperCase()}_TAG, 'max');
159
+ return { deleted: true as const };
160
+ });
161
+ `,Ln=({pascalName:e,camelName:t})=>`import { queryOptions, useSuspenseQuery } from '@tanstack/react-query';
390
162
 
391
- `:"",u=t.redux?`
392
- <Counter />`:"",d=`${p}export default function Home() {
393
- return (
394
- <div className="flex min-h-screen flex-col items-center justify-center p-24">
395
- <h1 className="text-4xl font-bold">Welcome to ${t.projectName}</h1>
396
- <p className="mt-4 text-xl">Get started by editing src/app/page.tsx</p>${u}
397
- </div>
398
- );
399
- }
400
- `;await h(Gr.join(e,a.ROOT_PAGE),d)};import Zc from"degit";var bn="teispace/nextjs-starter",In="v1.1.0",Qc=()=>In==="main"?bn:`${bn}#${In}`,J=async(e,t={})=>{await Zc(Qc(),{cache:!1,force:!0,verbose:t.verbose??!1}).clone(e)};var $n=async e=>{let t=S("Downloading template...");try{await J(e,{verbose:!0}),t.succeed("Template downloaded successfully.")}catch(r){throw t.fail("Failed to download template."),r}};var Ur=["npm","yarn","pnpm","bun"],ep=e=>{if(e!==void 0){if(!Ur.includes(e))throw new Error(`Invalid --package-manager "${e}". Valid: ${Ur.join(", ")}.`);return e}},kn=e=>{e.command("init").description("Initialize a new Next.js project").argument("[name]","Project name").option("-y, --yes","Skip all prompts and use the recommended production defaults").option("--package-manager <pm>",`Override the package manager (one of: ${Ur.join(", ")})`).action(async(t,r)=>{try{let i=ep(r.packageManager);await rp(t,{yes:r.yes??!1,packageManager:i})}catch(i){E(`${i instanceof Error?i.message:i}`),process.exit(1)}})},st=async(e,t,r,i)=>{let n=S(e);i.current=n;try{let o=await r();return n.succeed(t),o}finally{i.current===n&&(i.current=null)}},tp=e=>{let t=[];e.darkMode&&t.push("dark mode"),e.redux&&t.push("redux"),e.i18n&&t.push("i18n"),e.tests&&t.push("tests"),e.reactCompiler&&t.push("react-compiler"),e.preCommitHooks&&t.push("pre-commit hooks"),e.commitizen&&t.push("commitizen"),e.httpClient!=="none"&&t.push(`http (${e.httpClient})`),m(ee.dim("Defaults applied (run `next-maker remove <feature>` to undo any of them):")),m(ee.dim(` package manager : ${e.packageManager}`)),m(ee.dim(` features : ${t.join(", ")}`)),m("")},rp=async(e,t)=>{Gi();let r;t.yes?(r=Ji(e,{packageManager:t.packageManager}),m(ee.cyan(`\u2728 Bootstrapping ${r.projectName} with the recommended defaults`)),m(""),tp(r)):(m("Welcome to the Teispace Next.js App Creator!"),m(""),r=await Wi(e),t.packageManager&&(r={...r,packageManager:t.packageManager}));let i=Hr.resolve(process.cwd(),r.projectName);l(i)&&(console.error(ee.red(`Error: Directory ${r.projectName} already exists.`)),process.exit(1));let n={current:null},o=async()=>{if(l(i)){n.current?.stop(),n.current=null,console.log(ee.yellow(`
401
- Cleaning up: Deleting directory ${r.projectName}...`));try{await v(i),console.log(ee.green("Cleanup successful."))}catch(c){console.error(ee.red(`Failed to clean up directory ${r.projectName}:`),c)}}},s=ji(async()=>{console.log(ee.red(`
402
- Process interrupted. Cleaning up...`)),await o()});try{if(await $n(i),await Sn(i,r),await vn(i,r),await st("Generating providers and layout...","Providers and layout generated.",async()=>{await Cn(i,r),await Rn(i,r)},n),await st("Setting up developer tools...","Developer tools configured.",()=>Tn(i,r),n),await st("Initializing Git...","Git initialized.",()=>Ui(i,r.gitRemote,r.pushToRemote),n),await st("Installing dependencies...","Dependencies installed.",()=>Bi(i,r.packageManager),n),await st("Formatting and linting...","Formatted.",()=>K(i,r.packageManager,"lint:fix"),n),r.copyEnv){let c=Hr.join(i,".env.example"),p=Hr.join(i,".env");l(c)&&await(await import("node:fs/promises")).copyFile(c,p)}s(),m(""),m(ee.green(`\u2728 Project ${r.projectName} created successfully!`)),m(""),m("To get started:"),m(ee.cyan(` cd ${r.projectName}`)),m(ee.cyan(` ${r.packageManager==="npm"?"npm run dev":`${r.packageManager} dev`}`)),m("")}catch(c){s(),n.current?.fail("Failed to create project."),n.current=null,console.error(c),await o(),process.exit(1)}};import ri from"node:path";import le from"picocolors";import Mt from"node:path";var Nn=/^[A-Za-z][A-Za-z0-9-]*$/,ip=/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;function An(e){return ip.test(e)}function W(e,t="name"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);if(!Nn.test(r))throw new Error(`Invalid ${t} "${e}". Use letters, digits, and hyphens only \u2014 no slashes, spaces, "..", or absolute paths.`);if(An(r))throw new Error(`Invalid ${t} "${e}". That is a reserved name on Windows.`);return r}function pe(e,t="path"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);let i=r.replace(/^\.\//,"");if(i.startsWith("/")||/^[a-zA-Z]:/.test(i))throw new Error(`Invalid ${t} "${e}". Must be a relative path inside the project (no leading "/" or drive letter).`);for(let n of i.split("/")){if(!Nn.test(n))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(An(n))throw new Error(`Invalid ${t} "${e}". Segment "${n}" is reserved on Windows.`)}return i}function Gt(e,...t){let r=Mt.resolve(e),i=Mt.resolve(r,...t),n=Mt.relative(r,i);if(n===""||n===".")return i;if(n.startsWith("..")||Mt.isAbsolute(n))throw new Error(`Refusing to write outside the project directory (resolved path escapes "${r}").`);return i}q();import{mkdir as jn,writeFile as zr}from"node:fs/promises";import Ne from"node:path";w();var Br=e=>{let{componentName:t,isClient:r,hasI18n:i}=e,n=e.translationNamespace||t,o=[];return(r||i)&&o.push("'use client';"),i&&o.push("import { useTranslations } from 'next-intl';"),o.push(""),o.push(`export function ${t}() {`),i?(o.push(` const t = useTranslations('${n}');`),o.push(""),o.push(" return ("),o.push(" <div>"),o.push(" <h2>{t('title')}</h2>"),o.push(" </div>"),o.push(" );")):(o.push(" return ("),o.push(" <div>"),o.push(` <h2>${t}</h2>`),o.push(" </div>"),o.push(" );")),o.push("}"),o.push(""),o.join(`
403
- `)},Dn=e=>`export { ${e.componentName} } from './${e.componentName}';
404
- `;var Vr=async e=>{let{name:t,projectPath:r,isClient:i,hasI18n:n,featurePath:o}=e,s=N(t);o?await ap(s,r,o,i,n):await cp(s,t,r,i,n)},ap=async(e,t,r,i,n)=>{let o=Ne.join(t,r,"components");await jn(o,{recursive:!0}),await zr(Ne.join(o,`${e}.tsx`),Br({componentName:e,isClient:i,hasI18n:n}))},cp=async(e,t,r,i,n)=>{let o=Ne.join(r,"src","components","common",e);await jn(o,{recursive:!0}),await zr(Ne.join(o,`${e}.tsx`),Br({componentName:e,isClient:i,hasI18n:n})),await zr(Ne.join(o,"index.ts"),Dn({componentName:e}));let s=Ne.join(r,"src","components","common","index.ts");await pp(s,`export { ${e} } from './${e}';
405
- `);let c=Ne.join(r,"src","components","index.ts");await lp(c)},pp=async(e,t)=>{let r="";l(e)&&(r=await g(e)),r.includes(t.trim())||(r+=t,await h(e,r))},lp=async e=>{let t="";l(e)&&(t=await g(e));let r=`export * from './common';
406
- `;t.includes(r.trim())||(t+=r,await h(e,t))};import{writeFile as mp}from"node:fs/promises";import dp from"node:path";var Ln=e=>{let{camelName:t,pascalName:r,httpClient:i}=e,n=i==="axios"?"import { axiosClient } from '@/lib/utils/http';":"import { fetchClient } from '@/lib/utils/http';",o=i==="axios"?"axiosClient":"fetchClient";return`import { AppApis } from '@/lib/config';
407
- ${n}
408
- import type { ResultAsync } from '@/types';
163
+ import { AppApis } from '@/lib/config/app-apis';
164
+ import { http } from '@/lib/http';
165
+ import { unwrapForQuery } from '@/lib/query';
409
166
 
410
- /**
411
- * Summary type used in list responses (e.g., cards, tables).
412
- */
413
- export interface ${r}Summary {
414
- id: string;
415
- // Add list/card fields here (title, thumbnail, status, etc.)
416
- }
167
+ import { ${t}Keys } from './keys';
168
+ import { ${t}ListSchema, ${t}Schema } from './schema';
417
169
 
418
170
  /**
419
- * Detail type used in single-item responses (e.g., detail page).
171
+ * Client-side reads. A Server Component can \`prefetchQuery(${t}ListQuery())\`
172
+ * inside \`<HydrateQueries>\` so the hook below renders with data on first paint.
420
173
  */
421
- export interface ${r}Detail extends ${r}Summary {
422
- // Add detailed fields here (content, metadata, relations, etc.)
423
- }
424
-
425
- export interface Create${r}Dto {
426
- // Add create fields here
427
- }
174
+ export const ${t}ListQuery = () =>
175
+ queryOptions({
176
+ queryKey: ${t}Keys.list(),
177
+ queryFn: ({ signal }) =>
178
+ http.get(AppApis.${t}.list, { schema: ${t}ListSchema, signal }).then(unwrapForQuery),
179
+ });
428
180
 
429
- export interface Update${r}Dto {
430
- // Add update fields here
431
- }
181
+ export const ${t}Query = (id: string) =>
182
+ queryOptions({
183
+ queryKey: ${t}Keys.detail(id),
184
+ queryFn: ({ signal }) =>
185
+ http.get(AppApis.${t}.detail(id), { schema: ${t}Schema, signal }).then(unwrapForQuery),
186
+ });
432
187
 
433
- export const ${t}Service = {
434
- getAll: (): ResultAsync<${r}Summary[]> => {
435
- return ${o}.get<${r}Summary[]>(AppApis.${t}.getAll);
436
- },
188
+ export const use${e}List = () => useSuspenseQuery(${t}ListQuery());
189
+ export const use${e} = (id: string) => useSuspenseQuery(${t}Query(id));
190
+ `,Un=({camelName:e,kebabName:t})=>` ${e}: {
191
+ list: '/${t}',
192
+ detail: (id: string) => \`/${t}/\${id}\`,
193
+ create: '/${t}',
194
+ update: (id: string) => \`/${t}/\${id}\`,
195
+ delete: (id: string) => \`/${t}/\${id}\`,
196
+ },`;var nt=async e=>{let{name:t,featurePath:r,withActions:n}=e,o={kebabName:t,camelName:U(t),pascalName:$(t)},s=sr.join(r,"api");await Yi(s,{recursive:!0});let i=[["schema.ts",Fn(o)],["keys.ts",Mn(o)],["server.ts",_n(o)],["queries.ts",Ln(o)]];return n&&i.push(["actions.ts",Dn(o)]),await Promise.all(i.map(([a,p])=>Qi(sr.join(s,a),p))),i.map(([a])=>sr.join("api",a))};import{mkdir as Bn,writeFile as ar}from"node:fs/promises";import $e from"node:path";var ir=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
+ `)},Gn=e=>`export { ${e.componentName} } from './${e.componentName}';
198
+ `;var cr=async e=>{let{name:t,projectPath:r,isClient:n,hasI18n:o,featurePath:s}=e,i=$(t);s?await Zi(i,r,s,n,o):await ea(i,t,r,n,o)},Zi=async(e,t,r,n,o)=>{let s=$e.join(t,r,"components");await Bn(s,{recursive:!0}),await ar($e.join(s,`${e}.tsx`),ir({componentName:e,isClient:n,hasI18n:o}))},ea=async(e,t,r,n,o)=>{let s=$e.join(r,"src","components","common",e);await Bn(s,{recursive:!0}),await ar($e.join(s,`${e}.tsx`),ir({componentName:e,isClient:n,hasI18n:o})),await ar($e.join(s,"index.ts"),Gn({componentName:e}));let i=$e.join(r,"src","components","common","index.ts");await ta(i,`export { ${e} } from './${e}';
199
+ `);let a=$e.join(r,"src","components","index.ts");await ra(a)},ta=async(e,t)=>{let r="";y(e)&&(r=await j(e)),r.includes(t.trim())||(r+=t,await k(e,r))},ra=async e=>{let t="";y(e)&&(t=await j(e));let r=`export * from './common';
200
+ `;t.includes(r.trim())||(t+=r,await k(e,t))};import{mkdir as mr,writeFile as De}from"node:fs/promises";import H from"node:path";import{mkdir as na,writeFile as Pe}from"node:fs/promises";import ke from"node:path";var lr=e=>{let{sliceName:t,withPersist:r,state:n="redux"}=e;return n==="zustand"?`export * from './${t}.store';
201
+ `:`export * from './${t}.selectors';
202
+ export * from './${t}.slice';${r?`
203
+ export * from './persist';`:""}
204
+ `},Vn=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 { ${pr(t)}Keys } from './api/keys';`),a.push(`export { ${pr(t)}ListQuery, ${pr(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
+ `)}
206
+ `},qn=e=>`import 'server-only';
437
207
 
438
- getById: (id: string): ResultAsync<${r}Detail> => {
439
- return ${o}.get<${r}Detail>(AppApis.${t}.getById(id));
440
- },
208
+ export { get${e.pascalName}, list${e.pascalName}s, ${e.pascalName.toUpperCase()}_TAG } from './api/server';
209
+ `,pr=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase());var Jn=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';
441
210
 
442
- create: (data: Create${r}Dto): ResultAsync<${r}Detail> => {
443
- return ${o}.post<${r}Detail>(AppApis.${t}.create, data);
444
- },
211
+ import type { ${t}State } from '${n}';
445
212
 
446
- update: (id: string, data: Update${r}Dto): ResultAsync<${r}Detail> => {
447
- return ${o}.patch<${r}Detail>(AppApis.${t}.update(id), data);
448
- },
213
+ const ${o} = 1;
449
214
 
450
- delete: (id: string): ResultAsync<void> => {
451
- return ${o}.delete<void>(AppApis.${t}.delete(id));
452
- },
453
- };
454
- `},Mn=e=>{let{camelName:t,kebabName:r}=e;return` ${t}: {
455
- base: '/${r}',
456
- getAll: '/${r}',
457
- getById: (id: string) => \`/${r}/\${id}\`,
458
- create: '/${r}',
459
- update: (id: string) => \`/${r}/\${id}\`,
460
- delete: (id: string) => \`/${r}/\${id}\`,
461
- },`};var Jr=async e=>{let{name:t,outputPath:r,httpClient:i}=e,n=G(t),o=N(t);await mp(dp.join(r,`${t}.service.ts`),Ln({camelName:n,pascalName:o,httpClient:i}))};import{mkdir as hp,writeFile as zt}from"node:fs/promises";import ie from"node:path";import{writeFile as up}from"node:fs/promises";import fp from"node:path";var Gn=e=>{let{camelName:t,httpClient:r}=e;return`import { AppApis } from '@/lib/config';
462
- ${r==="axios"?"import { axiosClient } from '@/lib/utils/http';":"import { fetchClient } from '@/lib/utils/http';"}
463
- import type { ResultAsync } from '@/types';
464
-
465
- export const ${t}Service = {
466
- getAll: (): ResultAsync<string> => {
467
- return ${r==="axios"?"axiosClient":"fetchClient"}.get<string>(AppApis.${t}.getAll);
468
- },
469
- };
470
- `};var pt=async e=>{let{name:t,outputPath:r,httpClient:i}=e,n=G(t);await up(fp.join(r,`${t}.service.ts`),Gn({camelName:n,httpClient:i}))};import{mkdir as gp,writeFile as lt}from"node:fs/promises";import mt from"node:path";var Hn=e=>{let{sliceName:t,withPersist:r}=e;return`export * from './${t}.slice';
471
- export * from './${t}.selectors';
472
- export * from './${t}.types';${r?`
473
- export * from './persist';`:""}
474
- `},Un=e=>{let{featureName:t,componentName:r,hookName:i,withStore:n,withService:o}=e;return`export { default as ${r} } from './components/${r}';
475
- export { ${i} } from './hooks/${i}';
476
- export * from './types/${t}.types';${n?`
477
- export * from './store';`:""}${o?`
478
- export * from './services/${t}.service';`:""}
479
- `};var Bn=e=>{let{componentName:t,camelName:r,typesImportPath:i}=e,n=`${r.toUpperCase()}_PERSIST_VERSION`;return`import { createMigrate, type PersistConfig, type PersistedState } from 'redux-persist';
480
- import storage from '@/store/storage';
481
- import type { ${t}State } from '${i}';
482
-
483
- const ${n} = 1;
484
-
485
- const migrations = {
486
- // Bump ${n} and add a migration here when ${t}State's
487
- // shape changes. Receives the previously-persisted state, returns the new
488
- // shape. Returning \`undefined\` discards stale state.
489
- // 2: (state) => ({ ...state, newField: defaultValue }),
490
- } satisfies Record<number, (state: PersistedState) => PersistedState>;
491
-
492
- export const ${r}PersistConfig: PersistConfig<${t}State> = {
215
+ export const ${r}Persistence = definePersistence<${t}State>({
493
216
  key: '${r}',
494
- storage,
495
- version: ${n},
496
- // whitelist: ['someField'], // Specify which fields to persist
497
- migrate: createMigrate(migrations, { debug: false }),
498
- };
499
- `};var zn=e=>{let{componentName:t,camelName:r,sliceName:i}=e;return`import { RootState } from '@/store/rootReducer';
217
+ version: ${o},
218
+ // Fields that survive a reload. Transient status never should.
219
+ pick: [],
220
+ migrations: {
221
+ // Bump ${o} and add a step keyed by the new number when the
222
+ // persisted shape changes. Each step receives the previous version's data
223
+ // and returns the next; return \`undefined\` to discard.
224
+ // 2: (previous) => ({ ...(previous as ${t}State), newField: 0 }),
225
+ },
226
+ });
227
+ `};var zn=e=>{let{componentName:t,camelName:r}=e;return`import type { RootState } from '@/store/rootReducer';
500
228
 
501
229
  export const select${t}State = (state: RootState) => state.${r};
502
- export { setLoading, setError, resetState } from './${i}.slice';
503
- `};var Vn=e=>{let{componentName:t,camelName:r,typesImportPath:i}=e;return`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
504
- import type { ${t}State } from '${i}';
230
+ export const select${t}Status = (state: RootState) => state.${r}.status;
231
+ export const select${t}Error = (state: RootState) => state.${r}.error;
232
+ `};var Wn=e=>{let{componentName:t,camelName:r,typesImportPath:n}=e;return`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
233
+
234
+ import type { ${t}State } from '${n}';
505
235
 
506
236
  const initialState: ${t}State = {
507
- loading: false,
237
+ status: 'idle',
508
238
  error: null,
509
- // Add your initial state here
510
239
  };
511
240
 
512
241
  export const ${r}Slice = createSlice({
513
242
  name: '${r}',
514
243
  initialState,
515
244
  reducers: {
516
- setLoading: (state, action: PayloadAction<boolean>) => {
517
- state.loading = action.payload;
245
+ started: (state) => {
246
+ state.status = 'loading';
247
+ state.error = null;
518
248
  },
519
- setError: (state, action: PayloadAction<string | null>) => {
249
+ failed: (state, action: PayloadAction<string>) => {
250
+ state.status = 'error';
520
251
  state.error = action.payload;
521
252
  },
522
- resetState: (state) => {
523
- state.loading = false;
524
- state.error = null;
525
- },
253
+ reset: () => initialState,
526
254
  },
527
255
  });
528
256
 
529
- export const { setLoading, setError, resetState } = ${r}Slice.actions;
530
-
257
+ export const { started, failed, reset } = ${r}Slice.actions;
531
258
  export const ${r}Reducer = ${r}Slice.reducer;
532
- `};var Bt=e=>{let{componentName:t,withStore:r}=e;return`export interface ${t}State {
533
- ${r?`loading: boolean;
259
+ `};var Ot=e=>{let{componentName:t,withStore:r}=e;return r?`export type ${t}Status = 'idle' | 'loading' | 'error';
260
+
261
+ export interface ${t}State {
262
+ status: ${t}Status;
534
263
  error: string | null;
535
- // Add your state properties here`:`// Add your state properties here
536
- // example: value: string;`}
537
264
  }
538
- `};var dt=async e=>{let{name:t,outputPath:r,persist:i,typesImportPath:n=`./${t}.types`}=e;await gp(r,{recursive:!0});let o=N(t),s=G(t);await Promise.all([lt(mt.join(r,`${t}.types.ts`),Bt({componentName:o,withStore:!0})),lt(mt.join(r,`${t}.slice.ts`),Vn({componentName:o,camelName:s,typesImportPath:n})),lt(mt.join(r,`${t}.selectors.ts`),zn({componentName:o,camelName:s,sliceName:t})),...i?[lt(mt.join(r,"persist.ts"),Bn({componentName:o,camelName:s,typesImportPath:n}))]:[],lt(mt.join(r,"index.ts"),Hn({sliceName:t,withPersist:i}))])};var Jn=e=>{let{componentName:t,hookName:r}=e;return`'use client';
539
- import { ${r} } from '../hooks/${r}';
265
+ `:`export interface ${t}Props {
266
+ // Props for the ${t} component
267
+ }
268
+ `};var Hn=e=>{let{componentName:t,camelName:r,typesImportPath:n}=e;return`import type { StateCreator } from 'zustand';
269
+
270
+ import type { ${t}State } from '${n}';
271
+
272
+ export interface ${t}Slice {
273
+ ${r}: ${t}State;
274
+ ${r}Started: () => void;
275
+ ${r}Failed: (error: string) => void;
276
+ ${r}Reset: () => void;
277
+ }
540
278
 
279
+ const initial: ${t}State = { status: 'idle', error: null };
280
+
281
+ /**
282
+ * Slice creator. Compose it into the app store:
283
+ *
284
+ * // src/store/index.ts
285
+ * export type AppState = CounterSlice & ${t}Slice & { ... };
286
+ * ...create${t}Slice(set, get, api),
287
+ */
288
+ // biome-ignore lint/suspicious/noExplicitAny: composed into the app store, whose full type is declared there
289
+ export const create${t}Slice: StateCreator<any, [], [], ${t}Slice> = (set) => ({
290
+ ${r}: initial,
291
+ ${r}Started: () => set({ ${r}: { status: 'loading', error: null } }),
292
+ ${r}Failed: (error: string) => set({ ${r}: { status: 'error', error } }),
293
+ ${r}Reset: () => set({ ${r}: initial }),
294
+ });
295
+ `};var ot=async e=>{let{name:t,outputPath:r,persist:n,state:o="redux",typesImportPath:s=`./${t}.types`}=e;await na(r,{recursive:!0});let i=$(t),a=U(t),p=[];s===`./${t}.types`&&p.push(Pe(ke.join(r,`${t}.types.ts`),Ot({componentName:i,withStore:!0}))),o==="zustand"?p.push(Pe(ke.join(r,`${t}.store.ts`),Hn({componentName:i,camelName:a,typesImportPath:s})),Pe(ke.join(r,"index.ts"),lr({sliceName:t,withPersist:!1,state:o}))):(p.push(Pe(ke.join(r,`${t}.slice.ts`),Wn({componentName:i,camelName:a,typesImportPath:s})),Pe(ke.join(r,`${t}.selectors.ts`),zn({componentName:i,camelName:a,sliceName:t})),Pe(ke.join(r,"index.ts"),lr({sliceName:t,withPersist:n,state:o}))),n&&p.push(Pe(ke.join(r,"persist.ts"),Jn({componentName:i,camelName:a,typesImportPath:s})))),await Promise.all(p)};var Kn=e=>{let{componentName:t,hookName:r,withApi:n,pascalName:o,hasI18n:s}=e,i=s?`import { useTranslations } from 'next-intl';
296
+
297
+ `:"",a=s?` const t = useTranslations('${o}');
298
+ `:"",p=s?"{t('title')}":t;return n?`'use client';
299
+
300
+ ${i}import { use${o}List } from '../api/queries';
301
+
302
+ /**
303
+ * Renders hydrated query data: prefetch \`${oa(o)}ListQuery()\` in the
304
+ * page and wrap this component in \`<HydrateQueries>\` for a no-spinner first paint.
305
+ */
541
306
  export function ${t}() {
542
- const {} = ${r}();
307
+ ${a} const { data } = use${o}List();
543
308
 
544
309
  return (
545
- <div>
546
- <h2>${t} Component</h2>
547
- {/* Add your component UI here */}
548
- </div>
310
+ <section className="flex flex-col gap-2">
311
+ <h2 className="font-medium text-sm">${p}</h2>
312
+ <ul className="flex flex-col gap-1">
313
+ {data.map((item) => (
314
+ <li key={item.id}>{item.title}</li>
315
+ ))}
316
+ </ul>
317
+ </section>
549
318
  );
550
319
  }
320
+ `:`'use client';
551
321
 
552
- export default ${t};
553
- `};var Wn=e=>{let{hookName:t,componentName:r,featureName:i}=e;return`'use client';
554
- import { useAppDispatch, useAppSelector } from '@/store/hooks';
555
- import { select${r}State, setLoading, setError } from '../store/${i}.selectors';
322
+ ${i}import { ${r} } from '../hooks/${r}';
556
323
 
557
- export const ${t} = () => {
558
- const dispatch = useAppDispatch();
559
- const state = useAppSelector(select${r}State);
324
+ export function ${t}() {
325
+ ${a} const state = ${r}();
560
326
 
561
- const handleSetLoading = (loading: boolean) => {
562
- dispatch(setLoading(loading));
563
- };
327
+ return (
328
+ <section className="flex flex-col gap-2">
329
+ <h2 className="font-medium text-sm">${p}</h2>
330
+ <pre className="text-xs">{JSON.stringify(state, null, 2)}</pre>
331
+ </section>
332
+ );
333
+ }
334
+ `},oa=e=>e.charAt(0).toLowerCase()+e.slice(1);var Xn=e=>{let{hookName:t,componentName:r,featureName:n,state:o}=e;return o==="zustand"?`'use client';
564
335
 
565
- const handleSetError = (error: string | null) => {
566
- dispatch(setError(error));
567
- };
336
+ import { useAppStore } from '@/store/hooks';
568
337
 
569
- return {
570
- state,
571
- setLoading: handleSetLoading,
572
- setError: handleSetError,
573
- } as const;
338
+ export const ${t} = () => {
339
+ const status = useAppStore((state) => state.${st(n)}.status);
340
+ const error = useAppStore((state) => state.${st(n)}.error);
341
+ const start = useAppStore((state) => state.${st(n)}Started);
342
+ const fail = useAppStore((state) => state.${st(n)}Failed);
343
+ const reset = useAppStore((state) => state.${st(n)}Reset);
344
+
345
+ return { status, error, start, fail, reset } as const;
574
346
  };
575
- `},qn=e=>{let{hookName:t}=e;return`'use client';
576
- import { useState } from 'react';
347
+ `:`'use client';
348
+
349
+ import { useAppDispatch, useAppSelector } from '@/store/hooks';
350
+
351
+ import { failed, reset, started } from '../store/${n}.slice';
352
+ import { select${r}Error, select${r}Status } from '../store/${n}.selectors';
577
353
 
578
354
  export const ${t} = () => {
579
- // Add your state and logic here
580
- const [state, setState] = useState({});
355
+ const dispatch = useAppDispatch();
356
+ const status = useAppSelector(select${r}Status);
357
+ const error = useAppSelector(select${r}Error);
581
358
 
582
359
  return {
583
- state,
584
- // Add your methods here
360
+ status,
361
+ error,
362
+ start: () => dispatch(started()),
363
+ fail: (message: string) => dispatch(failed(message)),
364
+ reset: () => dispatch(reset()),
585
365
  } as const;
586
366
  };
587
- `};var Wr=async e=>{let{name:t,outputPath:r,createStore:i,persistStore:n,createService:o,httpClient:s}=e,c=N(t),p=`use${c}`,u=[ie.join(r,"components"),ie.join(r,"hooks"),ie.join(r,"types"),...i?[ie.join(r,"store")]:[],...o?[ie.join(r,"services")]:[]];await Promise.all(u.map(d=>hp(d,{recursive:!0}))),await Promise.all([zt(ie.join(r,"components",`${c}.tsx`),Jn({componentName:c,hookName:p})),zt(ie.join(r,"hooks",`${p}.ts`),i?Wn({hookName:p,componentName:c,featureName:t}):qn({hookName:p})),zt(ie.join(r,"types",`${t}.types.ts`),Bt({componentName:c,withStore:i})),zt(ie.join(r,"index.ts"),Un({featureName:t,componentName:c,hookName:p,withStore:i,withService:o}))]),i&&await dt({name:t,outputPath:ie.join(r,"store"),persist:n,typesImportPath:`../types/${t}.types`}),o&&s&&await pt({name:t,outputPath:ie.join(r,"services"),httpClient:s})};import{mkdir as yp,writeFile as xp}from"node:fs/promises";import qr from"node:path";var Xn=e=>{let{hookName:t,isClient:r}=e,i=[];return r&&i.push("'use client';"),i.push("import { useState, useCallback } from 'react';"),i.push(""),i.push(`export const ${t} = () => {`),i.push(" const [loading, setLoading] = useState(false);"),i.push(" const [error, setError] = useState<string | null>(null);"),i.push(""),i.push(" const execute = useCallback(async () => {"),i.push(" setLoading(true);"),i.push(" setError(null);"),i.push(" try {"),i.push(" // Add your logic here"),i.push(" } catch (err) {"),i.push(" setError(err instanceof Error ? err.message : 'An error occurred');"),i.push(" } finally {"),i.push(" setLoading(false);"),i.push(" }"),i.push(" }, []);"),i.push(""),i.push(" return {"),i.push(" loading,"),i.push(" error,"),i.push(" execute,"),i.push(" } as const;"),i.push("};"),i.push(""),i.join(`
588
- `)};var Xr=async e=>{let{name:t,projectPath:r,isClient:i,featurePath:n}=e,s=`use${N(t)}`,c;n?c=qr.join(r,n,"hooks"):c=qr.join(r,"src","hooks"),await yp(c,{recursive:!0}),await xp(qr.join(c,`${s}.ts`),Xn({hookName:s,isClient:i}))};import{mkdir as vp}from"node:fs/promises";import Vt from"node:path";w();var wp=e=>`import { setRequestLocale } from 'next-intl/server';
589
- import type { SupportedLocale } from '@/types/i18n';
367
+ `},st=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),At=e=>{let{hookName:t}=e;return`'use client';
590
368
 
591
- type Props = {
592
- children: React.ReactNode;
593
- params: Promise<{ locale: string }>;
594
- };
369
+ import { useCallback, useState } from 'react';
595
370
 
596
- export default async function ${e}({ children, params }: Props) {
597
- const { locale } = await params;
598
- setRequestLocale(locale as SupportedLocale);
371
+ export const ${t} = () => {
372
+ const [busy, setBusy] = useState(false);
373
+
374
+ const run = useCallback(async (task: () => Promise<void>) => {
375
+ setBusy(true);
376
+ try {
377
+ await task();
378
+ } finally {
379
+ setBusy(false);
380
+ }
381
+ }, []);
599
382
 
600
- return <>{children}</>;
601
- }
602
- `,Pp=e=>`type Props = {
603
- children: React.ReactNode;
383
+ return { busy, run } as const;
604
384
  };
385
+ `};var It=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';
605
386
 
606
- export default function ${e}({ children }: Props) {
607
- return <>{children}</>;
608
- }
609
- `,Yn=({componentName:e,hasI18n:t})=>t?wp(e):Pp(e);var Sp=/^[a-z][a-z0-9-]*$/,Ep=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,Tp=e=>{if(!Sp.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.`)},Cp=e=>{if(!Ep.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},Yr=async e=>{let{segment:t,at:r,projectPath:i,hasI18n:n,withGroup:o}=e;Tp(t),r&&Cp(r);let s=`${N(t)}Layout`,c=o?`(${t})`:t,p=["src","app"];n&&p.push("[locale]"),r&&p.push(...r.split("/")),p.push(c);let u=Vt.join(i,...p),d=Vt.join(u,"layout.tsx");if(l(d))throw new Error(`Layout already exists at ${Vt.relative(i,d)}.`);return await vp(u,{recursive:!0}),await h(d,Yn({componentName:s,hasI18n:n})),{layoutFile:d,displayPath:Vt.relative(i,d),componentName:s}};w();import{copyFile as Rp,readFile as bp,writeFile as Ip}from"node:fs/promises";import ut from"node:path";var Kr=async e=>{let{code:t,name:r,country:i,flag:n,projectPath:o,copyTranslations:s}=e;await $p(o,t,s),await kp(o,t),await Np(o,t,r,i,n)},$p=async(e,t,r)=>{let i=ut.join(e,"src","i18n","translations"),n=ut.join(i,"en.json"),o=ut.join(i,`${t}.json`);if(r)await Rp(n,o);else{let s=await bp(n,"utf-8"),c=JSON.parse(s),p=Kn(c);await Ip(o,`${JSON.stringify(p,null,2)}
610
- `)}},Kn=e=>{let t={};for(let[r,i]of Object.entries(e))typeof i=="object"&&i!==null?t[r]=Kn(i):t[r]="";return t},kp=async(e,t)=>{let r=ut.join(e,"src","types","i18n.ts");if(!l(r))return;let i=await g(r),n=/export type SupportedLocale = (.*);/,o=i.match(n);if(o&&!o[1].includes(`'${t}'`)){let s=`${o[1]} | '${t}'`;i=i.replace(n,`export type SupportedLocale = ${s};`),await h(r,i)}},Np=async(e,t,r,i,n)=>{let o=ut.join(e,"src","lib","config","app-locales.ts");if(!l(o))return;let s=await g(o);if(s.includes(`locale: '${t}'`))return;let c=s.lastIndexOf("];");if(c===-1)return;let p=` {
611
- name: '${r}',
612
- locale: '${t}',
613
- flag: '${n}',
614
- country: '${i}',
615
- },
616
- `;s=s.slice(0,c)+p+s.slice(c),await h(o,s)};import{mkdir as Ap,writeFile as Zr}from"node:fs/promises";import ft from"node:path";var Zn=e=>{let{componentName:t,routePath:r,hasI18n:i}=e;return i?`import { generateSEOMetadata } from '@/lib/config/seo';
617
- import type { Metadata } from 'next';
618
- import { getTranslations, setRequestLocale } from 'next-intl/server';
619
- import type { SupportedLocale } from '@/types/i18n';
387
+ import { makeTestQueryClient, renderWithProviders, screen } from '${n}';
388
+ import { ${i.keys} } from '${i.keysImport}';
389
+ import { ${t} } from '${r}';
620
390
 
621
- type Props = {
622
- params: Promise<{ locale: string }>;
623
- };
391
+ describe('${t}', () => {
392
+ it('renders hydrated data', () => {
393
+ const queryClient = makeTestQueryClient();
394
+ queryClient.setQueryData(${i.keys}.${i.key}(), [
395
+ { id: '1', title: 'First', createdAt: '2026-01-01T00:00:00.000Z' },
396
+ ]);
397
+ renderWithProviders(<${t} />, { queryClient${a.length?`, ${a.join(", ")}`:""} });
398
+ expect(screen.getByText('First')).toBeInTheDocument();
399
+ });
400
+ });
401
+ `;let p=a.length?`, { ${a.join(", ")} }`:"";return`import { describe, expect, it } from 'vitest';
624
402
 
625
- export async function generateMetadata(props: Props): Promise<Metadata> {
626
- const locale = (await props.params).locale as SupportedLocale;
627
- const t = await getTranslations({ locale, namespace: '${t}' });
403
+ import { renderWithProviders, screen } from '${n}';
404
+ import { ${t} } from '${r}';
628
405
 
629
- return generateSEOMetadata({
630
- title: t('title'),
631
- description: t('description'),
632
- path: '${r}',
406
+ describe('${t}', () => {
407
+ it('renders', () => {
408
+ renderWithProviders(<${t} />${p});
409
+ expect(screen.getByRole('heading')).toBeInTheDocument();
633
410
  });
634
- }
411
+ });
412
+ `},Yn=e=>{let{hookName:t,sourceImportPath:r,withStore:n,testUtilsImportPath:o}=e;return n?`// @vitest-environment jsdom
413
+ import { renderHook } from '@testing-library/react';
414
+ import { describe, expect, it } from 'vitest';
415
+
416
+ import { TestProviders } from '${o}';
417
+ import { ${t} } from '${r}';
635
418
 
636
- export default async function ${t}Page(props: Props) {
637
- const locale = (await props.params).locale as SupportedLocale;
638
- setRequestLocale(locale);
419
+ describe('${t}', () => {
420
+ it('returns a defined value', () => {
421
+ const { result } = renderHook(() => ${t}(), { wrapper: TestProviders });
422
+ expect(result.current).toBeDefined();
423
+ });
424
+ });
425
+ `:`// @vitest-environment jsdom
426
+ import { renderHook } from '@testing-library/react';
427
+ import { describe, expect, it } from 'vitest';
639
428
 
640
- const t = await getTranslations('${t}');
429
+ import { ${t} } from '${r}';
641
430
 
642
- return (
643
- <div>
644
- <h1>{t('title')}</h1>
645
- </div>
646
- );
647
- }
648
- `:`import type { Metadata } from 'next';
431
+ describe('${t}', () => {
432
+ it('returns a defined value', () => {
433
+ const { result } = renderHook(() => ${t}());
434
+ expect(result.current).toBeDefined();
435
+ });
436
+ });
437
+ `},Qn=e=>{let{camelName:t,sourceImportPath:r}=e,n=`${t}Slice`;return`import { describe, expect, it } from 'vitest';
649
438
 
650
- export const metadata: Metadata = {
651
- title: '${t}',
652
- description: '${t} page',
653
- };
439
+ import { failed, reset, ${n}, started } from '${r}';
654
440
 
655
- export default function ${t}Page() {
656
- return (
657
- <div>
658
- <h1>${t}</h1>
659
- </div>
660
- );
661
- }
662
- `},Qn=e=>{let{componentName:t,paramName:r,routePath:i,hasI18n:n}=e;return n?`import { generateSEOMetadata } from '@/lib/config/seo';
663
- import type { Metadata } from 'next';
664
- import { getTranslations, setRequestLocale } from 'next-intl/server';
665
- import type { SupportedLocale } from '@/types/i18n';
441
+ describe('${n}', () => {
442
+ const initialState = { status: 'idle', error: null } as const;
666
443
 
667
- type Props = {
668
- params: Promise<{ locale: string; ${r}: string }>;
444
+ it('returns the initial state', () => {
445
+ expect(${n}.reducer(undefined, { type: '@@INIT' })).toEqual(initialState);
446
+ });
447
+
448
+ it('tracks a start and a failure', () => {
449
+ const loading = ${n}.reducer(initialState, started());
450
+ expect(loading.status).toBe('loading');
451
+ const errored = ${n}.reducer(loading, failed('boom'));
452
+ expect(errored).toEqual({ status: 'error', error: 'boom' });
453
+ });
454
+
455
+ it('resets', () => {
456
+ expect(${n}.reducer({ status: 'error', error: 'boom' }, reset())).toEqual(initialState);
457
+ });
458
+ });
459
+ `};var dr=async e=>{let{name:t,outputPath:r,createStore:n,persistStore:o,createApi:s,state:i,hasI18n:a,hasTests:p}=e,m=$(t),c=s?`${m}List`:m,d=`use${m}`,u=n&&i!=="none";await mr(H.join(r,"components"),{recursive:!0}),await mr(H.join(r,"types"),{recursive:!0}),s||await mr(H.join(r,"hooks"),{recursive:!0});let f=[De(H.join(r,"components",`${c}.tsx`),Kn({componentName:c,hookName:d,withApi:s,pascalName:m,hasI18n:a})),De(H.join(r,"types",`${t}.types.ts`),Ot({componentName:m,withStore:u})),De(H.join(r,"index.ts"),Vn({featureName:t,componentName:c,hookName:d,pascalName:m,withStore:u,withApi:s}))];s||f.push(De(H.join(r,"hooks",`${d}.ts`),u?Xn({hookName:d,componentName:m,featureName:t,state:i==="zustand"?"zustand":"redux"}):At({hookName:d}))),s&&(f.push(nt({name:t,featurePath:r,withActions:!0})),f.push(De(H.join(r,"server.ts"),qn({pascalName:m})))),p&&f.push(De(H.join(r,"components",`${c}.test.tsx`),It({componentName:c,sourceImportPath:`./${c}`,testUtilsImportPath:"../../../../test/test-utils",hasState:i!=="none",hasI18n:a,withQueryData:s?{keysImport:"../api/keys",keys:`${sa(t)}Keys`,key:"list"}:void 0}))),await Promise.all(f),u&&await ot({name:t,outputPath:H.join(r,"store"),persist:o,state:i==="zustand"?"zustand":"redux",typesImportPath:`../types/${t}.types`})},sa=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase());import{mkdir as ia,writeFile as aa}from"node:fs/promises";import ur from"node:path";var fr=async e=>{let{name:t,projectPath:r,featurePath:n}=e,o=`use${$(t)}`,s=n?ur.join(r,n,"hooks"):ur.join(r,"src","hooks");await ia(s,{recursive:!0}),await aa(ur.join(s,`${o}.ts`),At({hookName:o}))};import{mkdir as ca}from"node:fs/promises";import Nt from"node:path";var Zn=({componentName:e})=>`type Props = {
460
+ children: React.ReactNode;
669
461
  };
670
462
 
671
- export async function generateMetadata(props: Props): Promise<Metadata> {
672
- const { locale, ${r} } = await props.params;
673
- const t = await getTranslations({ locale: locale as SupportedLocale, namespace: '${t}' });
463
+ // Layouts do not re-run on client navigation: put session checks in pages
464
+ // (\`requireUser\`) and data reads in the components that render them.
465
+ export default function ${e}({ children }: Props) {
466
+ return <>{children}</>;
467
+ }
468
+ `;var pa=/^[a-z][a-z0-9-]*$/,la=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,ma=e=>{if(!pa.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.`)},da=e=>{if(!la.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},gr=async e=>{let{segment:t,at:r,projectPath:n,hasI18n:o,withGroup:s}=e;ma(t),r&&da(r);let i=`${$(t)}Layout`,a=s?`(${t})`:t,p=["src","app"];o&&p.push("[locale]"),r&&p.push(...r.split("/")),p.push(a);let m=Nt.join(n,...p),c=Nt.join(m,"layout.tsx");if(y(c))throw new Error(`Layout already exists at ${Nt.relative(n,c)}.`);return await ca(m,{recursive:!0}),await k(c,Zn({componentName:i,hasI18n:o})),{layoutFile:c,displayPath:Nt.relative(n,c),componentName:i}};import{copyFile as ua,readFile as fa,writeFile as ga}from"node:fs/promises";import it from"node:path";var hr=async e=>{let{code:t,name:r,country:n,flag:o,projectPath:s,copyTranslations:i}=e;await ha(s,t,i),await ya(s,t),await wa(s,t,r,n,o)},ha=async(e,t,r)=>{let n=it.join(e,"src","i18n","translations"),o=it.join(n,"en.json"),s=it.join(n,`${t}.json`);if(r)await ua(o,s);else{let i=await fa(o,"utf-8"),a=JSON.parse(i),p=eo(a);await ga(s,`${JSON.stringify(p,null,2)}
469
+ `)}},eo=e=>{let t={};for(let[r,n]of Object.entries(e))typeof n=="object"&&n!==null?t[r]=eo(n):t[r]="";return t},ya=async(e,t)=>{let r=it.join(e,"src","types","i18n.ts");if(!y(r))return;let n=await j(r),o=/export const SUPPORTED_LOCALES = \[([^\]]*)\] as const;/,s=n.match(o);if(s){if(s[1].includes(`'${t}'`))return;let p=s[1].split(",").map(m=>m.trim()).filter(Boolean);p.push(`'${t}'`),n=n.replace(o,`export const SUPPORTED_LOCALES = [${p.join(", ")}] as const;`),await k(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 k(r,n))},wa=async(e,t,r,n,o)=>{let s=it.join(e,"src","lib","config","app-locales.ts");if(!y(s))return;let i=await j(s);if(i.includes(`locale: '${t}'`))return;let a=i.lastIndexOf("];");if(a===-1)return;let p=` {
470
+ name: '${r}',
471
+ locale: '${t}',
472
+ flag: '${o}',
473
+ country: '${n}',
474
+ ogLocale: '${t}_${t.toUpperCase()}',
475
+ },
476
+ `;i=i.slice(0,a)+p+i.slice(a),await k(s,i)};import{mkdir as ba,writeFile as yr}from"node:fs/promises";import Ft from"node:path";var xa=({routePath:e,hasI18n:t,paramName:r})=>`${t?"/[locale]":""}${e}${r?`/[${r}]`:""}`,to=e=>{let{componentName:t,routePath:r,hasI18n:n,paramName:o}=e,s=xa(e);return n?`import type { Metadata } from 'next';
477
+ import { getLocale, getTranslations } from 'next-intl/server';
478
+
479
+ import { generateSEOMetadata } from '@/lib/config/seo';
674
480
 
481
+ export async function generateMetadata(): Promise<Metadata> {
482
+ const [locale, t] = await Promise.all([getLocale(), getTranslations('${t}')]);
675
483
  return generateSEOMetadata({
676
484
  title: t('title'),
677
485
  description: t('description'),
678
- path: '${i}',
486
+ path: '${r}',
487
+ locale,
679
488
  });
680
489
  }
681
490
 
682
- export default async function ${t}Page(props: Props) {
683
- const { locale, ${r} } = await props.params;
684
- setRequestLocale(locale);
685
-
491
+ export default async function ${t}Page(${o?`{ params }: PageProps<'${s}'>`:""}) {
686
492
  const t = await getTranslations('${t}');
687
-
493
+ ${o?` const { ${o} } = await params;
494
+ `:""}
688
495
  return (
689
- <div>
690
- <h1>{t('title')}</h1>
691
- <p>${t}: {${r}}</p>
496
+ <div className="flex flex-col gap-2 p-6">
497
+ <h1 className="font-bold text-2xl">{t('title')}</h1>
498
+ <p className="text-gray-500 dark:text-gray-400">{t('description')}</p>${o?`
499
+ <p className="text-sm">${o}: {${o}}</p>`:""}
692
500
  </div>
693
501
  );
694
502
  }
695
503
  `:`import type { Metadata } from 'next';
696
504
 
697
- export const metadata: Metadata = {
505
+ import { generateSEOMetadata } from '@/lib/config/seo';
506
+
507
+ export const metadata: Metadata = generateSEOMetadata({
698
508
  title: '${t}',
699
509
  description: '${t} page',
700
- };
701
-
702
- type Props = {
703
- params: Promise<{ ${r}: string }>;
704
- };
510
+ path: '${r}',
511
+ });
705
512
 
706
- export default async function ${t}Page(props: Props) {
707
- const { ${r} } = await props.params;
513
+ export default ${o?"async ":""}function ${t}Page(${o?`{ params }: PageProps<'${s}'>`:""}) {
514
+ ${o?` const { ${o} } = await params;
708
515
 
709
- return (
710
- <div>
711
- <h1>${t}</h1>
712
- <p>${t}: {${r}}</p>
516
+ `:""} return (
517
+ <div className="flex flex-col gap-2 p-6">
518
+ <h1 className="font-bold text-2xl">${t}</h1>${o?`
519
+ <p className="text-sm">${o}: {${o}}</p>`:""}
713
520
  </div>
714
521
  );
715
522
  }
716
- `},eo=e=>`export default function ${e.componentName}Loading() {
523
+ `},ro=e=>`export default function ${e.componentName}Loading() {
717
524
  return (
718
- <div className="flex h-full items-center justify-center">
719
- <p>Loading...</p>
525
+ <div className="flex h-full items-center justify-center p-6">
526
+ <p className="text-gray-500 text-sm dark:text-gray-400">Loading\u2026</p>
720
527
  </div>
721
528
  );
722
529
  }
723
- `,to=e=>`'use client';
530
+ `,no=e=>`'use client';
531
+
532
+ import { useEffect } from 'react';
533
+
534
+ import { logger } from '@/lib/logger';
724
535
 
725
536
  export default function ${e.componentName}Error({
726
537
  error,
@@ -729,481 +540,410 @@ export default function ${e.componentName}Error({
729
540
  error: Error & { digest?: string };
730
541
  reset: () => void;
731
542
  }) {
543
+ useEffect(() => {
544
+ logger.error({ err: error, digest: error.digest }, '${e.componentName} failed to render');
545
+ }, [error]);
546
+
732
547
  return (
733
- <div className="flex h-full flex-col items-center justify-center gap-4">
734
- <h2>Something went wrong!</h2>
735
- <p>{error.message}</p>
736
- <button type="button" onClick={() => reset()}>Try again</button>
548
+ <div className="flex h-full flex-col items-center justify-center gap-4 p-6 text-center">
549
+ <h2 className="font-semibold text-xl">Something went wrong</h2>
550
+ <button
551
+ type="button"
552
+ onClick={reset}
553
+ className="cursor-pointer rounded-md bg-dark px-6 py-2 text-light text-sm dark:bg-light dark:text-dark"
554
+ >
555
+ Try again
556
+ </button>
737
557
  </div>
738
558
  );
739
559
  }
740
- `;var Qr=async e=>{let{name:t,projectPath:r,hasI18n:i,dynamic:n,withLoading:o=!1,withError:s=!1}=e,c=N(t),p=`/${t}`,u=i?"src/app/[locale]":"src/app",d;n?d=ft.join(r,u,t,`[${n}]`):d=ft.join(r,u,t),await Ap(d,{recursive:!0});let f=n?Qn({componentName:c,routePath:n?`${p}/[${n}]`:p,paramName:n,hasI18n:i}):Zn({componentName:c,routePath:p,hasI18n:i});await Zr(ft.join(d,"page.tsx"),f),o&&await Zr(ft.join(d,"loading.tsx"),eo({componentName:c})),s&&await Zr(ft.join(d,"error.tsx"),to({componentName:c}))};P();import{mkdir as _p}from"node:fs/promises";import ei from"node:path";w();var ro=(e,t)=>e.endsWith(t)?e:`${e}${t}`,io=({baseName:e})=>{let t=ro(e,"Provider"),r=t.replace(/Provider$/,""),i=`${r}ContextValue`,n=`${r}Context`,o=`use${r}`;return`'use client';
560
+ `;var wr=async e=>{let{name:t,projectPath:r,hasI18n:n,group:o,dynamic:s,withLoading:i=!1,withError:a=!1}=e,p=$(t),m=`/${t}`,c=[r,"src","app"];n&&c.push("[locale]"),o&&c.push(`(${o})`),c.push(t),s&&c.push(`[${s}]`);let d=Ft.join(...c);return await ba(d,{recursive:!0}),await yr(Ft.join(d,"page.tsx"),to({componentName:p,routePath:m,hasI18n:n,paramName:s})),i&&await yr(Ft.join(d,"loading.tsx"),ro({componentName:p})),a&&await yr(Ft.join(d,"error.tsx"),no({componentName:p})),{pageDir:d,routePath:s?`${m}/[${s}]`:m}};import{mkdir as va}from"node:fs/promises";import xr from"node:path";var oo=(e,t)=>e.endsWith(t)?e:`${e}${t}`,so=({baseName:e})=>{let t=oo(e,"Provider"),r=t.replace(/Provider$/,""),n=`${r}ContextValue`,o=`${r}Context`,s=`use${r}`;return`'use client';
741
561
  import { createContext, useContext, useMemo } from 'react';
742
562
 
743
- type ${i} = {
563
+ type ${n} = {
744
564
  // TODO: declare context value fields
745
565
  };
746
566
 
747
- const ${n} = createContext<${i} | null>(null);
567
+ const ${o} = createContext<${n} | null>(null);
748
568
 
749
- export const ${o} = (): ${i} => {
750
- const ctx = useContext(${n});
569
+ export const ${s} = (): ${n} => {
570
+ const ctx = useContext(${o});
751
571
  if (!ctx) {
752
- throw new Error('${o} must be used within ${t}');
572
+ throw new Error('${s} must be used within ${t}');
753
573
  }
754
574
  return ctx;
755
575
  };
756
576
 
757
577
  export const ${t} = ({ children }: { children: React.ReactNode }) => {
758
- const value = useMemo<${i}>(() => ({}), []);
759
- return <${n}.Provider value={value}>{children}</${n}.Provider>;
578
+ const value = useMemo<${n}>(() => ({}), []);
579
+ return <${o}.Provider value={value}>{children}</${o}.Provider>;
760
580
  };
761
- `},no=e=>ro(e,"Provider");var Fp=/^[A-Za-z][A-Za-z0-9-]*$/,Op=e=>e.includes("-")?N(e):e.charAt(0).toUpperCase()+e.slice(1),ti=async e=>{let{name:t,projectPath:r}=e;if(!Fp.test(t))throw new Error(`Invalid provider name "${t}". Use kebab-case or PascalCase letters and digits only.`);let i=Op(t).replace(/Provider$/,""),n=no(i),o=n,s=ei.join(r,a.PROVIDERS),c=ei.join(s,`${o}.tsx`);if(l(c))throw new Error(`Provider already exists at ${ei.relative(r,c)}.`);return await _p(s,{recursive:!0}),await h(c,io({baseName:i})),{componentName:n,fileBaseName:o,providerFile:c}};w();import gt from"node:path";var oo=e=>{let{componentName:t,sourceImportPath:r,testUtilsImportPath:i,hasRedux:n,hasI18n:o}=e,s=[];o&&s.push("messages: {}"),n&&s.push("preloadedState: {}");let c=s.length?`, {
762
- ${s.join(`,
763
- `)},
764
- }`:"";return`import { describe, expect, it } from 'vitest';
765
- import { renderWithProviders, screen } from '${i}';
766
- import { ${t} } from '${r}';
767
-
768
- describe('${t}', () => {
769
- it('renders without crashing', () => {
770
- renderWithProviders(<${t} />${c});
771
- // TODO: assert something meaningful about the rendered output.
772
- expect(screen).toBeDefined();
773
- });
774
- });
775
- `},so=e=>{let{hookName:t,sourceImportPath:r,withStore:i,testUtilsImportPath:n}=e;return i?`import { renderHook } from '@testing-library/react';
776
- import { describe, expect, it } from 'vitest';
777
- import { TestProviders } from '${n}';
778
- import { ${t} } from '${r}';
779
-
780
- describe('${t}', () => {
781
- it('returns a defined value', () => {
782
- const { result } = renderHook(() => ${t}(), { wrapper: TestProviders });
783
- expect(result.current).toBeDefined();
784
- });
785
- });
786
- `:`import { renderHook } from '@testing-library/react';
787
- import { describe, expect, it } from 'vitest';
788
- import { ${t} } from '${r}';
789
-
790
- describe('${t}', () => {
791
- it('returns a defined value', () => {
792
- const { result } = renderHook(() => ${t}());
793
- expect(result.current).toBeDefined();
794
- });
795
- });
796
- `},ao=e=>{let{camelName:t,sourceImportPath:r}=e,i=`${t}Slice`;return`import { describe, expect, it } from 'vitest';
797
- import { ${i}, resetState, setError, setLoading } from '${r}';
798
-
799
- describe('${i}', () => {
800
- const initialState = { loading: false, error: null } as const;
801
-
802
- it('returns the initial state', () => {
803
- expect(${i}.reducer(undefined, { type: '@@INIT' })).toEqual(initialState);
804
- });
805
-
806
- it('handles setLoading', () => {
807
- const next = ${i}.reducer(initialState, setLoading(true));
808
- expect(next.loading).toBe(true);
809
- });
810
-
811
- it('handles setError', () => {
812
- const next = ${i}.reducer(initialState, setError('boom'));
813
- expect(next.error).toBe('boom');
814
- });
815
-
816
- it('handles resetState', () => {
817
- const next = ${i}.reducer({ loading: true, error: 'boom' }, resetState());
818
- expect(next).toEqual(initialState);
819
- });
820
- });
821
- `};var ge=async e=>{let{projectPath:t,sourceFile:r,kind:i,symbolName:n}=e,o=gt.dirname(r),s=gt.basename(r).replace(/\.(tsx?|jsx?)$/i,""),c=i==="component"?".tsx":".ts",p=gt.join(o,`${s}.test${c}`),u=`./${s}`,d=Dp(o,gt.join(t,"test","test-utils")),f;return i==="component"?f=oo({componentName:n,sourceImportPath:u,testUtilsImportPath:d,hasRedux:!!e.hasRedux,hasI18n:!!e.hasI18n}):i==="hook"?f=so({hookName:n,sourceImportPath:u,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?d:void 0}):f=ao({camelName:n,sourceImportPath:u}),await h(p,f),p},Dp=(e,t)=>{let r=gt.relative(e,t).replace(/\\/g,"/");return r.startsWith(".")?r:`./${r}`};import jp from"enquirer";var{prompt:Lp}=jp,co=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"componentName",message:"Component name (kebab-case):",validate:n=>n?/^[a-z][a-z0-9-]*$/.test(n)?!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 i=r.length>0?await Lp(r):{};return{componentName:e??i.componentName,isClient:t.client??i.isClient??!1}};var po=e=>{e.command("component [name]").description("Generate a shared component").option("--client","Add 'use client' directive").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 i=process.cwd();m(le.cyan(`
581
+ `},io=e=>oo(e,"Provider");var $a=/^[A-Za-z][A-Za-z0-9-]*$/,Pa=e=>e.includes("-")?$(e):e.charAt(0).toUpperCase()+e.slice(1),br=async e=>{let{name:t,projectPath:r}=e;if(!$a.test(t))throw new Error(`Invalid provider name "${t}". Use kebab-case or PascalCase letters and digits only.`);let n=Pa(t).replace(/Provider$/,""),o=io(n),s=o,i=xr.join(r,C.PROVIDERS),a=xr.join(i,`${s}.tsx`);if(y(a))throw new Error(`Provider already exists at ${xr.relative(r,a)}.`);return await va(i,{recursive:!0}),await k(a,so({baseName:n})),{componentName:o,fileBaseName:s,providerFile:a}};import at from"node:path";var re=async e=>{let{projectPath:t,sourceFile:r,kind:n,symbolName:o}=e,s=at.dirname(r),i=at.basename(r).replace(/\.(tsx?|jsx?)$/i,""),a=n==="component"?".tsx":".ts",p=at.join(s,`${i}.test${a}`),m=`./${i}`,c=ka(s,at.join(t,"test","test-utils")),d;return n==="component"?d=It({componentName:o,sourceImportPath:m,testUtilsImportPath:c,hasState:!!e.hasState,hasI18n:!!e.hasI18n}):n==="hook"?d=Yn({hookName:o,sourceImportPath:m,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?c:void 0}):d=Qn({camelName:o,sourceImportPath:m}),await k(p,d),p},ka=(e,t)=>{let r=at.relative(e,t).replace(/\\/g,"/");return r.startsWith(".")?r:`./${r}`};import{readFile as Sa,writeFile as Ea}from"node:fs/promises";import Ra from"node:path";var Le=(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+`
582
+ `+e.slice(s+o.length)}return`${t}
583
+ ${e}`},vr=(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(p=>p.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(
584
+ ${o.join(`,
585
+ `)},
586
+ )`;return e.replace(r,i.length<=100?i:a)},$r=(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(", ")}]`))},Pr=(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()}
587
+ ${t}
588
+ ${o}`};var ct=async e=>{let{serviceName:t,projectPath:r}=e,n=U(t),o=Ra.join(r,"src","lib","config","app-apis.ts");try{let s=await Sa(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=Pr(s,Un({kebabName:t,camelName:n,pascalName:$(t)})),await Ea(o,s)}catch(s){throw new Error(`Failed to register API endpoints: ${s}`,{cause:s})}};import{readFile as ja,writeFile as Ca}from"node:fs/promises";import Ta from"node:path";var kr=async(e,t,r)=>{let n=Ta.join(e,"src","lib","config","app-paths.ts");if(!y(n))return;let o=await ja(n,"utf-8"),s=U(t);if(o.includes(`${s}:`))return;let i=o.lastIndexOf("} as const;");if(i===-1)return;let a=` ${s}: '${r}',
589
+ `;o=o.slice(0,i)+a+o.slice(i),await Ca(n,o)};import Sr from"node:path";var Oa=/^[A-Z][A-Z0-9_]*$/,po=e=>{if(!Oa.test(e))throw new Error(`Invalid env var name "${e}". Use UPPER_SNAKE_CASE (e.g. SENTRY_DSN, NEXT_PUBLIC_API_URL).`)},ne=(e,t)=>t?e.startsWith("NEXT_PUBLIC_")?e:`NEXT_PUBLIC_${e}`:e,Er=e=>e.public?"client":"server",Aa=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,"\\'")}')`}},Ia=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()"}},lo=e=>{let t=ne(e.name,e.public),r=Ia(e),n=e.default!==void 0,o=!e.required&&!n;if(n&&(r+=Aa(e)),o&&(r+=".optional()"),e.description){let s=e.description.replace(/'/g,"\\'");r+=`
590
+ .describe('${s}')`}return` ${t}: ${r},`},mo=e=>{let t=ne(e.name,e.public);return` ${t}: process.env.${t},`},Na=e=>new RegExp(`(${e}:\\s*\\{)([\\s\\S]*?)(\\n\\s*\\})`),ao=/(runtimeEnv:\s*\{)([\s\S]*?)(\n\s*\})/,co=(e,t,r,n)=>{let o=t.replace(/\s+$/,""),s=o.length===0||/,\s*$/.test(o)?o:`${o},`;return`${e}${s}
591
+ ${n}
592
+ ${r.replace(/^\n/,"")}`},uo=(e,t)=>{let r=ne(t.name,t.public),n=Er(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=Na(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,p,m]=i,c=lo(t),d=e.replace(s,co(a,p,m,c)),u=d.match(ao);if(!u)throw new Error("Could not locate the `runtimeEnv: { \u2026 }` map in src/lib/env/index.ts.");let[,f,h,w]=u;return d=d.replace(ao,co(f,h,w,mo(t))),d},Fa="# -public",Ma=e=>{let t=ne(e.name,e.public),r=e.default??"",n=e.public?` ${Fa}`:"";return`${t}=${r}${n}`},fo=(e,t)=>{let r=ne(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let o=e===""||e.endsWith(`
593
+ `)?"":`
594
+ `,s=e===""?"":`
595
+ `,i=t.description?`# ${t.description}
596
+ `:"";return`${e}${o}${s}${i}${Ma(t)}
597
+ `},go=(e,t)=>{let r=ne(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let o=e===""||e.endsWith(`
598
+ `)?"":`
599
+ `,s=t.default??"";return`${e}${o}${r}=${s}
600
+ `},Rr=async(e,t)=>{po(t.name);let r=Sr.join(e,"src/lib/env/index.ts");if(!y(r))throw new Error("src/lib/env/index.ts not found \u2014 run `next-maker init` first.");let n=await j(r),o=uo(n,t),s=o!==n;s&&await k(r,o);let i=Sr.join(e,C.ENV_EXAMPLE),a=!1;if(y(i)){let c=await j(i),d=fo(c,t);a=d!==c,a&&await k(i,d)}let p=Sr.join(e,".env"),m=!1;if(y(p)){let c=await j(p),d=go(c,t);m=d!==c,m&&await k(p,d)}return{schemaUpdated:s,envExampleUpdated:a,envUpdated:m,group:Er(t)}};import Mt from"node:path";var _a=e=>new RegExp(`<\\s*${e}\\b`),ho=(e,t)=>{if(_a(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}>
601
+ ${s}{children}
602
+ ${o}</${t}>`;return e.replace(r,i)},yo=(e,t)=>{let r=`export * from './${t}';`;if(e.includes(r))return e;let n=e.split(`
603
+ `),o=[],s=[];for(let d of n)/^export \* from '\.\/.*';\s*$/.test(d)?o.push(d):s.push(d);let i=[...o,r].sort(),a=n.findIndex(d=>/^export \* from '\.\//.test(d));if(a===-1)return`${r}
604
+ ${e.replace(/^\n+/,"")}`;let p=n.slice(0,a),m=n.findIndex((d,u)=>u>a&&!/^export \* from '\.\//.test(d)),c=m===-1?[]:n.slice(m);return[...p,...i,...c].join(`
605
+ `)},Da=C.PROVIDERS,La=C.ROOT_PROVIDER,Ua=C.PROVIDERS_INDEX,wo=async(e,t={fileExists:y,readFile:j})=>{let r=Mt.join(e,La);if(t.fileExists(r))return r;let n=Mt.join(e,Da);if(!t.fileExists(n))return null;let{readdir:o}=await import("node:fs/promises"),i=(await o(n).catch(()=>[])).filter(p=>p.endsWith(".tsx")),a=null;for(let p of i){let m=Mt.join(n,p),c=await t.readFile(m);if(!/\{children\}/.test(c))continue;let u=((c.split("{children}")[0]??"").match(/<[A-Z][A-Za-z0-9_]*\b/g)??[]).length;(!a||u>a.depth)&&(a={file:m,depth:u})}return a?.file??null},jr=async e=>{let{projectPath:t,componentName:r,fileBaseName:n}=e,o=await wo(t);if(o){let a=await j(o),p=a;(!/from '\.\//.test(a)||!a.includes(`{ ${r} }`))&&(p=Le(p,`import { ${r} } from './${n}';`)),p=ho(p,r),p!==a&&await k(o,p)}let s=Mt.join(t,Ua),i=null;if(y(s)){let a=await j(s),p=yo(a,n);p!==a&&await k(s,p),i=s}return{rootProviderFile:o,barrelFile:i}};import{readFile as xo,writeFile as bo}from"node:fs/promises";import vo from"node:path";var pt=async e=>{let{projectPath:t,name:r,persist:n,importPath:o}=e,s=vo.join(t,"src","store","rootReducer.ts"),i=vo.join(t,"src","store","index.ts"),a=U(r),p=o.replace(/^src\//,"@/");try{let m=await xo(s,"utf-8");if(m=Le(m,`import { ${a}Slice } from '${p}';`),m=vr(m,`${a}Slice`),await bo(s,m),n&&y(i)){let c=await xo(i,"utf-8");c=Le(c,`import { ${a}Persistence } from '${p}';`),c=$r(c,`${a}Persistence`),await bo(i,c)}}catch(m){throw new Error(`Failed to register slice '${r}': ${m}`,{cause:m})}};import{readFile as Ga,writeFile as Ba}from"node:fs/promises";import Va from"node:path";var Se=async(e,t)=>{let r=Va.join(e,"src","i18n","translations","en.json");if(!y(r))return;let n=await Ga(r,"utf-8"),o=JSON.parse(n);o[t]||(o[t]={title:t,description:`${t} page description`},await Ba(r,`${JSON.stringify(o,null,2)}
606
+ `))};import qa from"enquirer";var{prompt:Ja}=qa,$o=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 Ja(r):{};return{componentName:e??n.componentName,isClient:t.client??n.isClient??!1}};var Po=e=>{e.command("component [name]").description("Generate a shared component").option("--client","Add 'use client' directive").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();l(K.cyan(`
822
607
  \u{1F9E9} Component Generator
823
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected");let o=await co(t,{client:r.client});W(o.componentName,"component name"),r.feature&&pe(r.feature,"feature path");let s=N(o.componentName),c=o.isClient,p=r.i18n??!1,u=Mp(r,n.hasTests);y.start("Generating component..."),await Vr({name:o.componentName,projectPath:i,isClient:c||p,hasI18n:p,featurePath:r.feature}),y.succeed("Component generated");let d=null;if(u){let x=r.feature?ri.join(i,r.feature,"components",`${s}.tsx`):ri.join(i,"src/components/common",s,`${s}.tsx`);y.start("Generating test..."),d=await ge({projectPath:i,sourceFile:x,kind:"component",symbolName:s,hasRedux:n.hasRedux,hasI18n:n.hasI18n}),y.succeed("Test generated")}let f=r.feature?`${r.feature}/components/${s}.tsx`:`src/components/common/${s}/${s}.tsx`;m(le.green(`
824
- \u2728 Component '${s}' created successfully!
825
- `)),m(le.dim(` \u{1F4C4} ${f}`)),r.feature||(m(le.dim(` \u{1F4C4} src/components/common/${s}/index.ts`)),m(le.dim(" \u{1F4DD} Updated src/components/common/index.ts")),m(le.dim(" \u{1F4DD} Updated src/components/index.ts"))),d&&m(le.dim(` \u{1F9EA} ${ri.relative(i,d)}`)),m(""),m(le.cyan("Usage:")),r.feature?m(le.dim(` import { ${s} } from '${r.feature.replace(/^src\//,"@/")}/components/${s}';`)):m(le.dim(` import { ${s} } from '@/components';`)),m("")}catch(i){y.fail("Component generation failed"),E(`${i}`),process.exit(1)}})},Mp=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import $ from"picocolors";P();w();import Vp from"node:path";P();import mo from"node:path";import Gp from"picocolors";w();var lo=/export default (.*?);/,ii="ANALYZE=true next build",Jt=e=>e.includes("@next/bundle-analyzer"),ht=e=>{if(Jt(e))return e;let t=`import withBundleAnalyzer from '@next/bundle-analyzer';
826
- ${e}`,r=t.match(lo);if(!r)throw new Error("Could not locate `export default` in next.config.ts.");let n=`const bundleAnalyzer = withBundleAnalyzer({
827
- enabled: process.env.ANALYZE === 'true',
828
- });
829
-
830
- export default bundleAnalyzer(${r[1]});`;return t.replace(lo,n)};var Wt=async e=>{let t=S("Adding @next/bundle-analyzer...");try{let r=mo.join(e,a.NEXT_CONFIG);if(!l(r))throw new Error(`${a.NEXT_CONFIG} not found.`);let i=await g(r);if(Jt(i)){t.fail("@next/bundle-analyzer is already configured.");return}await h(r,ht(i)),await _(mo.join(e,a.PACKAGE_JSON),n=>(n.scripts=n.scripts??{},n.scripts.analyze||(n.scripts.analyze=ii),n)),t.text="Installing @next/bundle-analyzer...",await At(e,"@next/bundle-analyzer"),t.succeed(Gp.green("@next/bundle-analyzer configured."))}catch(r){throw t.fail("Failed to add @next/bundle-analyzer."),r}};P();import Bp from"node:path";import zp from"picocolors";w();P();import Hp from"node:path";w();var qt=(e,t)=>e.filter(r=>r.kind==="missingPackage"&&r.depKind===t).map(r=>r.name),Up=e=>e.filter(t=>t.kind==="missingFile").map(t=>t.file),F=(e,t)=>Up(e).some(r=>r===t||r.startsWith(`${t}/`));var U=(e,t)=>e.some(r=>r.kind==="missingInjection"&&(t===void 0||r.file===t)),B=async(e,t)=>{let r=qt(t,"dependency"),i=qt(t,"devDependency");if(r.length===0&&i.length===0)return[];let n=await A(e);return r.length>0&&await Ir(e,n,r),i.length>0&&await fe(e,n,i),[...r,...i]},Ee=async(e,t)=>{let r=new Map;for(let n of t)(n.kind==="missingScript"&&n.expected||n.kind==="mismatchedScript")&&r.set(n.name,n.expected);if(r.size===0)return[];let i=Hp.join(e,a.PACKAGE_JSON);return l(i)?(await _(i,n=>{let o={...n.scripts??{}};for(let[s,c]of r)o[s]=c;return{...n,scripts:o}}),[...r.keys()]):[]},te=async(e,t)=>{await J(e);try{return await t(e)}finally{await v(e)}};var uo=async(e,t)=>{let r=S("Repairing Bundle Analyzer...");try{if(U(t,a.NEXT_CONFIG)){let s=Bp.join(e,a.NEXT_CONFIG);if(l(s)){let c=await g(s),p=ht(c);p!==c&&await h(s,p)}}let i=await B(e,t),n=await Ee(e,t),o=[i.length>0?`reinstalled ${i.join(", ")}`:null,n.length>0?`restored ${n.join(", ")}`:null].filter(Boolean);r.succeed(zp.green(o.length>0?`Bundle Analyzer repaired (${o.join("; ")}).`:"Bundle Analyzer repaired."))}catch(i){throw r.fail("Failed to repair Bundle Analyzer."),i}};var O=(e,t)=>async(r,i)=>{if(i&&i.length>0){await t(r,i);return}await e(r)};var ni=(e,t)=>{let r=e.split(`
831
- `),i=[],n=new RegExp(`^import\\s+${t}\\s+from\\s+['"][^'"]+['"];?\\s*$`),o=/^(import\s+)\{([^}]+)\}(\s+from\s+['"][^'"]+['"];?\s*)$/;for(let s of r){if(n.test(s))continue;let c=s.match(o);if(c){let p=c[2].split(",").map(d=>d.trim()).filter(Boolean),u=p.filter(d=>d.replace(/^type\s+/,"").split(/\s+as\s+/)[0].trim()!==t);if(u.length===0)continue;if(u.length<p.length){i.push(`${c[1]}{ ${u.join(", ")} }${c[3]}`);continue}}i.push(s)}return i.join(`
832
- `)},Ve=e=>t=>{let r=t.split(`
833
- `),i=new RegExp(`^([ \\t]*)<${e}\\b`),n=-1,o="";for(let f=0;f<r.length;f++){let x=r[f].match(i);if(x){n=f,o=x[1];break}}if(n===-1||!/>\s*$/.test(r[n]))return null;let s=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),c=new RegExp(`^${s}<\\/${e}>\\s*$`),p=-1;for(let f=n+1;f<r.length;f++)if(c.test(r[f])){p=f;break}if(p===-1)return null;let u=r.slice(n+1,p).map(f=>f.startsWith(" ")?f.slice(2):f),d=[...r.slice(0,n),...u,...r.slice(p+1)].join(`
834
- `);return ni(d,e)};var fo=e=>{let t=e.split(`
835
- `).filter(i=>!/^import\s+createNextIntlPlugin\s+from\s+['"]next-intl\/plugin['"];?\s*$/.test(i)).join(`
836
- `);t=t.replace(/^const\s+withNextIntl\s*=\s*createNextIntlPlugin\([^)]*\);\s*\n?/m,"");let r=/withNextIntl\(\s*((?:[^()]|\([^()]*\))+?)\s*\)/;return r.test(t)?(t=t.replace(r,"$1"),t):null},go=e=>{let t=ni(e,"withBundleAnalyzer"),r=/^const\s+bundleAnalyzer\s*=\s*withBundleAnalyzer\(\{[\s\S]*?\}\);\s*\n?/m;if(!r.test(t))return null;t=t.replace(r,"");let i=/bundleAnalyzer\(\s*((?:[^()]|\([^()]*\))+?)\s*\)/;return i.test(t)?(t=t.replace(i,"$1"),t):null};var ho={id:"bundle-analyzer",name:"Bundle Analyzer",description:"@next/bundle-analyzer wrapped around the default export",detect:async e=>{let t=Vp.join(e,a.NEXT_CONFIG);return l(t)?/@next\/bundle-analyzer/.test(await g(t)):!1},files:[],packages:[{name:"@next/bundle-analyzer",kind:"devDependency"}],scripts:[{name:"analyze",expectedValue:"ANALYZE=true next build"}],injections:[{file:a.NEXT_CONFIG,description:"withBundleAnalyzer import + wrapping",presence:/@next\/bundle-analyzer/,removePattern:go}],apply:O(Wt,uo)};P();w();import Xp from"node:path";P();import yo from"node:path";import Jp from"picocolors";w();var yt=`${JSON.stringify({path:"cz-conventional-changelog"},null,2)}
837
- `;var oi=e=>{let t={...e.scripts??{}},r={...e.devDependencies??{}};return t.commit||(t.commit="cz"),r[R.COMMITIZEN]||(r[R.COMMITIZEN]="*"),r[R.CZ_CONVENTIONAL_CHANGELOG]||(r[R.CZ_CONVENTIONAL_CHANGELOG]="*"),{...e,scripts:t,devDependencies:r}},si=e=>{let t=e.devDependencies??{},r=[];return t[R.COMMITIZEN]||r.push(R.COMMITIZEN),t[R.CZ_CONVENTIONAL_CHANGELOG]||r.push(R.CZ_CONVENTIONAL_CHANGELOG),r};var Xt=async e=>{let t=S("Setting up Commitizen...");try{let r=yo.join(e,a.CZRC);l(r)||await h(r,yt);let i=yo.join(e,a.PACKAGE_JSON),n=JSON.parse(await g(i)),o=si(n);if(await _(i,s=>oi(s)),o.length>0){t.text=`Installing ${o.join(", ")}...`;let s=await A(e);await fe(e,s,o)}t.succeed(Jp.green("Commitizen configured. Use `commit` to start a guided commit."))}catch(r){throw t.fail("Failed to set up Commitizen."),r}};P();import Wp from"node:path";import qp from"picocolors";w();var xo=async(e,t)=>{let r=S("Repairing Commitizen...");try{let i=!1;if(F(t,a.CZRC)){let c=Wp.join(e,a.CZRC);l(c)||(await h(c,yt),i=!0)}let n=await B(e,t),o=await Ee(e,t),s=[i?`rewrote ${a.CZRC}`:null,n.length>0?`reinstalled ${n.join(", ")}`:null,o.length>0?`restored ${o.join(", ")}`:null].filter(Boolean);r.succeed(qp.green(s.length>0?`Commitizen repaired (${s.join("; ")}).`:"Commitizen repaired."))}catch(i){throw r.fail("Failed to repair Commitizen."),i}};var wo={id:"commitizen",name:"Commitizen",description:"Guided conventional-commit prompt via `commit` script",detect:async e=>l(Xp.join(e,a.CZRC)),files:[{path:a.CZRC,generated:!0}],packages:[{name:"commitizen",kind:"devDependency"},{name:"cz-conventional-changelog",kind:"devDependency"}],scripts:[{name:"commit",expectedValue:"cz"}],injections:[],apply:O(Xt,xo)};P();w();import el from"node:path";import Yp from"node:path";import Kp from"picocolors";w();P();import Po from"node:path";w();var vo=async(e,t)=>{t.text="Fetching assets from starter repo...",await J(e)},So=async(e,t)=>{let r=Po.join(e,a.THEME_PROVIDER),i=Po.join(t,"src/providers/CustomThemeProvider.tsx");await b(i,r)};P();w();import xt from"node:path";P();w();import Eo from"node:path";var Yt=async e=>{let t=[Eo.join(e,a.ROOT_LAYOUT),Eo.join(e,"src/app/[locale]/layout.tsx")];for(let r of t)if(l(r)&&(await g(r)).includes("<body"))return r;for(let r of t)if(l(r))return r;return""};var To=async e=>{let t=xt.join(e,a.THEME_PROVIDER),r=xt.join(e,a.GLOBALS_CSS),i=xt.join(e,"package.json");if(l(t))return{isSetup:!0,reason:"CustomThemeProvider.tsx exists"};if(l(i)){let n=JSON.parse(await g(i)),o="@teispace/next-themes";if(n.dependencies?.[o]||n.devDependencies?.[o])return{isSetup:!0,reason:`${o} is installed`}}return l(r)&&(await g(r)).includes("@custom-variant dark")?{isSetup:!0,reason:"Dark theme CSS found in globals.css"}:{isSetup:!1,reason:""}},Co=async e=>{let t=xt.join(e,a.GLOBALS_CSS),r=xt.join(e,a.PROVIDERS_INDEX),i=await Yt(e);if(!l(t)||!i||!l(r))throw new Error("Project structure mismatch. Ensure src/styles/globals.css, src/app/layout.tsx (or src/app/[locale]/layout.tsx), and src/providers/index.ts exist.");return i};P();w();import Kt from"node:path";var Zt=async e=>{let t=Kt.join(e,a.PROVIDERS_INDEX),r=await g(t);r.includes("CustomThemeProvider")||(r+=`export * from './CustomThemeProvider';
838
- `,await h(t,r))},Qt=async e=>{let t=Kt.join(e,"src/providers/RootProvider.tsx");if(!l(t))return;let r=await g(t),i="'use client';",n=r.includes(i);if(n&&(r=r.replace(i,"").trim()),r.includes("CustomThemeProvider")||(/import\s*\{[^}]*\}\s*from\s*'@\/providers'/.test(r)?r=r.replace(/(import\s*\{)([^}]*)(\}\s*from\s*'@\/providers')/,(o,s,c,p)=>`${s}${c.trim()?`${c.trim()}, CustomThemeProvider `:" CustomThemeProvider "}${p}`):r=`import { CustomThemeProvider } from '@/providers';
839
- ${r}`),n&&(r=`${i}
840
- ${r}`),!r.includes("<CustomThemeProvider"))if(r.includes("{children}"))r=r.replace(/\{children\}/,"<CustomThemeProvider>{children}</CustomThemeProvider>");else{let o=r.match(/return\s*\(\s*([\s\S]*?)\s*\);/);o&&(r=r.replace(o[0],`return (
841
- <CustomThemeProvider>
842
- ${o[1]}
843
- </CustomThemeProvider>
844
- );`))}await h(t,r)},Ro=async(e,t)=>{let r=Kt.join(e,a.GLOBALS_CSS),i=Kt.join(t,"src/styles/globals.css"),n="";if(l(i)){let s=await g(i),c=s.match(/@custom-variant dark \(.*?\);/),p=s.match(/@theme \{[\s\S]*?\}/);c&&(n+=`
845
- ${c[0]}
846
- `),p&&(n+=`
847
- ${p[0]}
848
- `)}n||(n=`
849
- @custom-variant dark (&:where(.dark, .dark *));
850
-
851
- @theme {
852
- --color-dark: #202938;
853
- --color-light: #f5f5f5;
854
- }
855
- `);let o=await g(r);if(!o.includes("@custom-variant dark")){if(o.includes("@import")){let s=o.lastIndexOf("@import"),c=o.indexOf(`
856
- `,s);o=o.slice(0,c+1)+n+o.slice(c+1)}else o=n+o;await h(r,o)}},bo=async e=>{let t=await g(e);t.includes("dark:bg-dark")||(t=t.replace(/className="([^"]*)"/,'className="$1 bg-light dark:bg-dark"'),await h(e,t))};var er=async e=>{let t=S("Setting up Dark Theme..."),r=Yp.join(e,".next-maker-temp");try{let{isSetup:i,reason:n}=await To(e);if(i){t.fail(`Dark Theme is already set up (${n}).`);return}let o=await Co(e);await vo(r,t),await So(e,r),await Zt(e),await Qt(e),await Ro(e,r),await bo(o),t.text="Installing @teispace/next-themes...",await z(e,"@teispace/next-themes"),t.text="Formatting code...";let s=await A(e);await K(e,s,"lint:fix"),t.succeed(Kp.green("Dark Theme setup successfully!"))}catch(i){throw t.fail("Failed to setup Dark Theme."),i}finally{await v(r)}};P();import tr from"node:path";import Zp from"picocolors";w();var Qp="src/providers/RootProvider.tsx",Io=async(e,t)=>{let r=S("Repairing Dark Theme..."),i=tr.join(e,".next-maker-temp-dark-theme-repair");try{let n=tr.join(e,a.THEME_PROVIDER),o=[];F(t,a.THEME_PROVIDER)&&!l(n)&&(r.text="Fetching CustomThemeProvider from starter repo...",await te(i,async p=>{await b(tr.join(p,a.THEME_PROVIDER),n)}),o.push(a.THEME_PROVIDER)),U(t,Qp)&&(r.text="Restoring <CustomThemeProvider> wrap...",l(tr.join(e,a.PROVIDERS_INDEX))&&await Zt(e),await Qt(e),o.push("<CustomThemeProvider> wrap")),r.text="Restoring dependencies...";let s=await B(e,t),c=[o.length>0?`restored ${o.join(", ")}`:null,s.length>0?`reinstalled ${s.join(", ")}`:null].filter(Boolean);r.succeed(Zp.green(c.length>0?`Dark Theme repaired (${c.join("; ")}).`:"Dark Theme repaired."))}catch(n){throw r.fail("Failed to repair Dark Theme."),n}};var $o={id:"dark-theme",name:"Dark Theme",description:"@teispace/next-themes + CustomThemeProvider",detect:async e=>l(el.join(e,a.THEME_PROVIDER)),files:[{path:a.THEME_PROVIDER,generated:!0}],packages:[{name:"@teispace/next-themes",kind:"dependency"}],scripts:[],injections:[{file:"src/providers/RootProvider.tsx",description:"<CustomThemeProvider> wrap",presence:/CustomThemeProvider/,removePattern:Ve("CustomThemeProvider")}],apply:O(er,Io)};import tl from"node:fs/promises";import ir from"node:path";import nr from"enquirer";import ai from"picocolors";w();P();import re from"node:fs/promises";import T from"node:path";w();var ko=async(e,t)=>{t.text="Downloading assets from starter repository...",await J(e)},No=async(e,t,r,i)=>{let n=T.join(e,a.HTTP_UTILS),o=T.join(t,a.HTTP_UTILS);await re.cp(T.join(t,a.ERRORS_DIR),T.join(e,a.ERRORS_DIR),{recursive:!0});let s=T.join(e,a.APP_APIS);l(s)||await b(T.join(t,a.APP_APIS),s);let c=T.join(e,a.API_URL_FILE);l(c)||await b(T.join(t,a.API_URL_FILE),c);let p=T.join(e,a.CONSTANTS);if(!l(p))await b(T.join(t,a.CONSTANTS),p);else{let d=await g(p),f=!1;d.includes("API_RESPONSE_DATA_KEY")||(d+=`
857
- export const API_RESPONSE_DATA_KEY = 'data';
858
- `,f=!0),d.includes("SAVE_AUTH_TOKENS")||(d+=`
859
- export const SAVE_AUTH_TOKENS = false;
860
- `,f=!0),f&&await h(p,d)}let u=T.join(e,a.STORAGE_SERVICE);if(l(u)||await re.cp(T.join(t,a.STORAGE_SERVICE),u,{recursive:!0}),await re.cp(T.join(t,a.COMMON_TYPES_DIR),T.join(e,a.COMMON_TYPES_DIR),{recursive:!0}),await re.cp(T.join(t,a.UTILITY_TYPES_DIR),T.join(e,a.UTILITY_TYPES_DIR),{recursive:!0}),await b(T.join(o,"token-store.ts"),T.join(n,"token-store.ts")),await b(T.join(o,"client-utils.ts"),T.join(n,"client-utils.ts")),await re.cp(T.join(t,a.HTTP_SHARED),T.join(e,a.HTTP_SHARED),{recursive:!0,force:!0}),await b(T.join(o,"server.ts"),T.join(n,"server.ts")),await re.cp(T.join(t,a.HTTP_BUNDLE_SENTINEL_DIR),T.join(e,a.HTTP_BUNDLE_SENTINEL_DIR),{recursive:!0,force:!0}),i&&i.length>0)for(let d of i){let f=T.join(e,d==="fetch"?a.FETCH_CLIENT:a.AXIOS_CLIENT);await v(f)}for(let d of r)d==="fetch"?await re.cp(T.join(t,a.FETCH_CLIENT),T.join(e,a.FETCH_CLIENT),{recursive:!0}):await re.cp(T.join(t,a.AXIOS_CLIENT),T.join(e,a.AXIOS_CLIENT),{recursive:!0});await b(T.join(o,"index.ts"),T.join(n,"index.ts")),await b(T.join(t,a.HTTP_TYPES),T.join(e,a.HTTP_TYPES))},Ao=async e=>{let{isFileUsed:t,isStringUsed:r,stripSaveAuthTokens:i}=await Promise.resolve().then(()=>(Be(),rn));await v(T.join(e,a.HTTP_UTILS));let n=T.join(e,a.STORAGE_SERVICE);l(n)&&(await r(e,"SecureStorageService",[n])||await v(n));let o=T.join(e,a.API_URL_FILE);l(o)&&(await r(e,"getApiBaseUrl",[o])||await re.unlink(o));let s=T.join(e,a.CONSTANTS);if(l(s)){let d=await g(s),f=!1;if(await r(e,"API_RESPONSE_DATA_KEY",[s])||(d=d.replace(/export const API_RESPONSE_DATA_KEY = .*;\n?/g,""),f=!0),!await r(e,"SAVE_AUTH_TOKENS",[s])){let H=i(d);H!==d&&(d=H,f=!0)}f&&(d=`${d.replace(/\n\s*\n/g,`
861
-
608
+ `)),g.start("Detecting project setup...");let o=await T(n);g.succeed("Project setup detected");let s=await $o(t,{client:r.client});F(s.componentName,"component name"),r.feature&&W(r.feature,"feature path");let i=$(s.componentName),a=s.isClient,p=r.i18n??!1,m=za(r,o.hasTests);g.start("Generating component..."),await cr({name:s.componentName,projectPath:n,isClient:a||p,hasI18n:p,featurePath:r.feature}),g.succeed("Component generated"),p&&await Se(n,i);let c=null;if(m){let u=r.feature?Cr.join(n,r.feature,"components",`${i}.tsx`):Cr.join(n,"src/components/common",i,`${i}.tsx`);g.start("Generating test..."),c=await re({projectPath:n,sourceFile:u,kind:"component",symbolName:i,hasState:o.state!=="none",hasI18n:o.hasI18n}),g.succeed("Test generated")}let d=r.feature?`${r.feature}/components/${i}.tsx`:`src/components/common/${i}/${i}.tsx`;l(K.green(`
609
+ \u2728 Component '${i}' created successfully!
610
+ `)),l(K.dim(` \u{1F4C4} ${d}`)),r.feature||(l(K.dim(` \u{1F4C4} src/components/common/${i}/index.ts`)),l(K.dim(" \u{1F4DD} Updated src/components/common/index.ts")),l(K.dim(" \u{1F4DD} Updated src/components/index.ts"))),c&&l(K.dim(` \u{1F9EA} ${Cr.relative(n,c)}`)),l(""),l(K.cyan("Usage:")),r.feature?l(K.dim(` import { ${i} } from '${r.feature.replace(/^src\//,"@/")}/components/${i}';`)):l(K.dim(` import { ${i} } from '@/components';`)),l("")}catch(n){g.fail("Component generation failed"),x(`${n}`),process.exit(1)}})},za=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import{spawnSync as Wa}from"node:child_process";import M from"picocolors";var Ha=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}`}},ko=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 ee(r),s=await Me(o,{starterPath:t.starterPath});n=s.dispose;let i=await rr(r,o,s),a=0;if(t.fix&&i.some(c=>c.drift.length)){let c=await Sn(r,{...o,answers:{}},s,o.answers),d=new Set(i.flatMap(h=>h.drift.filter(w=>w.kind==="missingFile").map(w=>w.file)));c.copy=c.copy.filter(h=>d.has(h)),c.remove=[],c.unwrapJsx=[],c.unwrapCall=[],c.removePackages=[],c.removeScripts=[],c.manual=[];let u=new Set(i.flatMap(h=>h.drift.filter(w=>w.kind==="missingPackage").map(w=>w.name)));for(let h of Object.keys(c.addPackages))u.has(h)||delete c.addPackages[h];for(let h of Object.keys(c.addDevPackages))u.has(h)||delete c.addDevPackages[h];let f=new Set(i.flatMap(h=>h.drift.filter(w=>w.kind==="missingScript").map(w=>w.name)));for(let h of Object.keys(c.addScripts))f.has(h)||delete c.addScripts[h];await En(r,s,c,o),a=c.copy.length+Object.keys(c.addPackages).length+Object.keys(c.addDevPackages).length+Object.keys(c.addScripts).length,i=await rr(r,o,s)}let p;if(t.compile){let[c,...d]=ae(o.packageManager,"type-check").split(" "),u=Wa(c,d,{cwd:r,encoding:"utf-8"});p={ok:u.status===0,output:`${u.stdout}${u.stderr}`.trim()}}let m=i.filter(c=>c.on&&c.drift.length);t.json&&(l(JSON.stringify({starter:o.starter,results:i,fixed:a,compile:p},null,2)),process.exit(m.length||p?.ok===!1?1:0)),l(M.cyan(`
611
+ \u{1FA7A} Project Doctor
612
+ `)),l(M.dim(` ${o.starter.name} ${o.starter.version} \xB7 ${s.source.location}
613
+ `));for(let c of i){if(!c.on){l(` ${M.dim("\u2014")} ${M.dim(c.id)} ${M.dim("(off)")}`);continue}if(c.drift.length===0){l(` ${M.green("\u2713")} ${c.id}`);continue}l(` ${M.yellow("!")} ${M.bold(c.id)} ${M.dim(`(${c.drift.length} issue${c.drift.length===1?"":"s"})`)}`);for(let d of c.drift)l(` ${M.yellow("\u2022")} ${Ha(d)}`)}a&&l(M.green(`
614
+ restored ${a} item(s) from the starter`)),p&&(l(""),l(p.ok?M.green(" \u2713 type-check passed"):M.red(" \u2717 type-check failed")),p.ok||l(M.dim(p.output.split(`
615
+ `).slice(-30).join(`
616
+ `)))),l(""),m.length===0&&p?.ok!==!1&&(l(M.green(`\u2713 No drift detected.
617
+ `)),process.exit(0)),m.length&&!t.fix&&l(M.dim(`Run with --fix to restore missing pieces from the starter.
618
+ `)),process.exit(1)}catch(o){x(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{await n?.()}})};import Ue from"picocolors";var So=new Set(["string","url","number","boolean","enum"]),Ka=e=>{let t=(e??"string").toLowerCase();if(!So.has(t))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(So).join(", ")}.`);return t},Xa=e=>{if(!e)return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length===0?void 0:t},Eo=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();if(l(Ue.cyan(`
619
+ \u{1F510} Env Var Generator
620
+ `)),r.required&&r.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let o=Ka(r.type),s=Xa(r.enum);if(o==="enum"&&(!s||s.length===0))throw new Error("--type=enum requires at least one value via --enum a,b,c.");let i={name:t,type:o,required:!!r.required,default:r.default,description:r.describe,enumValues:s,public:!!r.public};g.start("Updating src/lib/env/index.ts, .env.example, and .env...");let a=await Rr(n,i);g.succeed("Env var written");let p=ne(i.name,i.public);l(Ue.green(`
621
+ \u2728 ${p} declared in the ${a.group} group.
622
+ `)),l(Ue.dim("Touched:")),l(Ue.dim(` ${a.schemaUpdated?"\u270F\uFE0F ":"\u23ED "} src/lib/env/index.ts`)),l(Ue.dim(` ${a.envExampleUpdated?"\u270F\uFE0F ":"\u23ED "} .env.example`)),l(Ue.dim(` ${a.envUpdated?"\u270F\uFE0F ":"\u23ED "} .env`)),l("")}catch(n){g.fail("Env var declaration failed"),x(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Lt from"node:path";import Re from"picocolors";import Ya from"enquirer";var{prompt:Qa}=Ya,Ro=async(e={})=>e.source?{source:e.source}:{source:(await Qa({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 Za,readFile as ec,stat as tc}from"node:fs/promises";import Ee from"node:path";var jo=async e=>{try{return await Za(e),!0}catch{return!1}},rc=async e=>{try{return(await tc(e)).isDirectory()}catch{return!1}},_t=async e=>{let t=[Ee.join(e,"src","app"),Ee.join(e,"app")];for(let r of t)if(await rc(r))return r;return null},nc=["next-pwa","@ducanh2912/next-pwa","@serwist/next","serwist"],Tr=async e=>{let t={isPwa:!1},r=[Ee.join(e,"public","manifest.json"),Ee.join(e,"public","manifest.webmanifest"),Ee.join(e,"public","site.webmanifest")];for(let o of r)if(await jo(o)){t.publicManifestPath=o,t.isPwa=!0;break}let n=await _t(e);if(n)for(let o of["ts","tsx","js"]){let s=Ee.join(n,`manifest.${o}`);if(await jo(s)){t.appManifestPath=s,t.isPwa=!0;break}}try{let o=JSON.parse(await ec(Ee.join(e,"package.json"),"utf-8")),s={...o.dependencies,...o.devDependencies};for(let i of nc)if(s[i]){t.framework=i,t.isPwa=!0;break}}catch{}return t};import{mkdir as cc,readFile as Do,stat as pc,writeFile as lc}from"node:fs/promises";import X from"node:path";import mc from"sharp";import sc from"png-to-ico";import lt from"sharp";var Co=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),Or=(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 oc(t)}},oc=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="${Co(n)}" fill="white"/></svg>`},To=(e,t)=>`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${e}"><rect width="${e}" height="${e}" fill="${Co(t)}"/></svg>`;var Ge=async e=>{let{size:t,paddingPercent:r,bg:n,fit:o,shape:s,radiusPercent:i,quality:a,forceSquare:p,sourceBuffer:m}=e,c=Math.max(1,Math.round(t*(1-r*2/100))),d=o==="cover"?"cover":"contain",u=await lt(m,{density:384}).resize(c,c,{fit:d,background:{r:0,g:0,b:0,alpha:0}}).png().toBuffer(),f=Math.round((t-c)/2),h=n==="transparent"?{r:0,g:0,b:0,alpha:0}:await Ar(n),w=await lt({create:{width:t,height:t,channels:4,background:h}}).composite([{input:u,left:f,top:f}]).png().toBuffer();if(!p&&(s!=="square"||o==="clip")){let b=s==="square"?"rounded":s,S=Buffer.from(Or(b,t,i));w=await lt(w).composite([{input:S,blend:"dest-in"}]).png({compressionLevel:Oo(a)}).toBuffer()}else w=await lt(w).png({compressionLevel:Oo(a)}).toBuffer();return w},Ar=async e=>{let t=await lt(Buffer.from(To(1,e))).raw().toBuffer({resolveWithObject:!0}),[r,n,o,s=255]=t.data;return{r,g:n,b:o,alpha:s/255}},Oo=e=>{let t=Math.round((100-e)/100*9);return Math.max(0,Math.min(9,t))},Ao=async e=>{if(e.length===0)throw new Error("No frames provided to encodeIco");return sc(e)};import{readFile as ic,writeFile as xf}from"node:fs/promises";var Io=e=>e.map(({src:t,size:r})=>({src:t,sizes:`${r}x${r}`,type:"image/png",purpose:"any maskable"})),No=async(e,t)=>{let r=await ic(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)}
623
+ `};var Fo=e=>`icons: [
624
+ ${e.map(r=>` { src: '${r.src}', sizes: '${r.sizes}', type: '${r.type}'${r.purpose?`, purpose: '${r.purpose}'`:""} }`).join(`,
625
+ `)},
626
+ ],`,Mo=(e,t,r)=>`${JSON.stringify({name:e,short_name:e,start_url:"/",display:"standalone",background_color:"#ffffff",theme_color:t,icons:r},null,2)}
627
+ `;import{stat as ac}from"node:fs/promises";import Ir from"node:path";var _o=[".png",".jpg",".jpeg",".webp",".svg",".avif"],Dt=async e=>{if(!e)throw new Error("Source image path is required (use --path).");let t=Ir.isAbsolute(e)?e:Ir.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=Ir.extname(t).toLowerCase();if(!_o.includes(n))throw new Error(`Unsupported source format '${n}'. Use one of: ${_o.join(", ")}`);return{absolutePath:t,ext:n,bytes:r.size,isVector:n===".svg"}};var Nr=(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 dc=async e=>{try{return await pc(e),!0}catch{return!1}},uc=async(e,t,r)=>{let n=[];for(let o of e){if(await dc(o.outPath)&&!t&&!o.alwaysWrite){n.push({path:o.outPath,bytes:o.bytes.length,skipped:!0});continue}r||(await cc(X.dirname(o.outPath),{recursive:!0}),await lc(o.outPath,o.bytes)),n.push({path:o.outPath,bytes:o.bytes.length})}return n},Fr=async(e,t)=>{let r=await Dt(t.source),n=await Do(r.absolutePath),o=t.ogSource?await Do((await Dt(t.ogSource)).absolutePath):n,s=[],i=[];for(let c of t.icoSizes)i.push(await Ge({sourceBuffer:n,size:c,shape:"square",radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality,forceSquare:!0}));let a=await Ao(i);if(s.push({outPath:X.join(t.outDir,"favicon.ico"),bytes:a}),t.emitIcon){let c=await Ge({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:X.join(t.outDir,"icon.png"),bytes:c})}if(t.emitApple){let c=t.bg==="transparent"?"#ffffff":t.bg,d=await Ge({sourceBuffer:n,size:180,shape:t.shape,radiusPercent:t.radius,bg:c,paddingPercent:t.padding,fit:t.fit,quality:t.quality});s.push({outPath:X.join(t.outDir,"apple-icon.png"),bytes:d})}if(t.emitOg){let c=await Lo(o,t,!1);s.push({outPath:X.join(t.outDir,"opengraph-image.png"),bytes:c})}if(t.emitTwitter){let c=await Lo(o,t,!0);s.push({outPath:X.join(t.outDir,"twitter-image.png"),bytes:c})}let p;if(t.pwa||t.pwaInit){let c=await Tr(e);if(t.pwa&&!c.isPwa)throw new Error("No PWA setup detected. Use --pwa-init to bootstrap a manifest, or set up next-pwa/serwist first.");let d=X.join(e,"public"),u=[192,512],f=[];for(let w of u){let P=await Ge({sourceBuffer:n,size:w,shape:t.shape,radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality}),b=`icon-${w}.png`;s.push({outPath:X.join(d,b),bytes:P}),f.push({src:`/${b}`,size:w})}let h=Io(f);if(c.publicManifestPath){let w=await No(c.publicManifestPath,h);s.push({outPath:c.publicManifestPath,bytes:Buffer.from(w,"utf-8"),alwaysWrite:!0})}else if(c.appManifestPath)p=Fo(h);else if(t.pwaInit){let w=X.join(d,"manifest.webmanifest"),P=Mo(X.basename(e)||"App","#000000",h);s.push({outPath:w,bytes:Buffer.from(P,"utf-8")})}}return{outputs:await uc(s,t.force,t.dryRun),pwaSnippet:p}},Lo=async(e,t,r)=>{let o=r?600:630,s=Math.round(Math.min(1200,o)*(1-t.padding*2/100)),i=await Ge({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,p=await Ar(a),m=Math.round((1200-s)/2),c=Math.round((o-s)/2);return mc({create:{width:1200,height:o,channels:4,background:p}}).composite([{input:i,left:m,top:c}]).png().toBuffer()};var Uo=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 fc=["square","rounded","circle","squircle"],gc=["contain","cover","clip"],Go=(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},Mr=(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},Vo=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();l(Re.cyan(`
628
+ \u{1F3A8} Favicon Generator
629
+ `));let n=await Ro({source:t.path}),o=await hc(r,t.out),s=Go(t.shape,fc,"square","shape"),i=Go(t.fit,gc,"contain","fit"),a=Mr(t.radius,20,"radius",0,50),p=Mr(t.padding,0,"padding",0,30),m=Mr(t.quality,90,"quality",1,100),c=Nr(t.sizes,[16,32,48]),d=t.bg??"transparent";xc(d);let u=t.all===!0,f={source:n.source,outDir:o,force:!!t.force,dryRun:!!t.dryRun,emitIcon:!!t.icon||u,emitApple:!!t.apple||u,emitOg:!!t.og||u,emitTwitter:!!t.twitter,ogSource:t.ogSource,pwa:!!t.pwa,pwaInit:!!t.pwaInit,shape:s,radius:a,bg:d,padding:p,fit:i,icoSizes:c,quality:m};g.start("Generating favicon assets...");let h=await Fr(r,f);g.succeed(f.dryRun?"Favicon plan ready":"Favicon assets generated"),l("");for(let w of h.outputs){let P=Lt.relative(r,w.path);w.skipped?l(Re.yellow(` \u21B7 skipped ${P} (exists; use --force to overwrite)`)):f.dryRun?l(Re.dim(` \u{1F4C4} would write ${P} (${Bo(w.bytes)})`)):l(Re.green(` \u2713 ${P} `)+Re.dim(`(${Bo(w.bytes)})`))}h.pwaSnippet&&(l(""),l(Re.cyan("Add this to your typed manifest (src/app/manifest.ts):")),l(Re.dim(h.pwaSnippet))),l("")}catch(r){g.fail("Favicon generation failed");let n=r instanceof Error?r.message:String(r);x(n),process.exit(1)}})},hc=async(e,t)=>{if(t)return Lt.isAbsolute(t)?t:Lt.resolve(e,t);let r=await _t(e);return r||(cn("Could not detect app/ or src/app/ \u2014 falling back to src/app"),Lt.join(e,"src","app"))},yc=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,wc=/^(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/]+\)$/i,xc=e=>{if(e!=="transparent"&&!yc.test(e)&&!wc.test(e)&&!Uo.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().`)},Bo=e=>e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`;import _r from"node:path";import _ from"picocolors";import bc from"enquirer";var{prompt:vc}=bc,qo=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 vc(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 zo=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();l(_.cyan(`
630
+ \u{1F3AF} Feature Generator
631
+ `)),g.start("Detecting project setup...");let o=await T(n);g.succeed("Project setup detected"),l(_.dim(` state: ${o.state} i18n: ${o.hasI18n?"\u2713":"\u2717"} tests: ${o.hasTests?"\u2713":"\u2717"}
632
+ `));let s=await qo(t,o.state,{api:r.api,store:r.store,persist:r.persist});F(s.featureName,"feature name"),r.path&&W(r.path,"path");let i=r.path||_r.join("src","features"),a=te(n,i,s.featureName);await ve(n,s.featureName,i)&&(x(`Feature '${s.featureName}' already exists at ${i}.`),process.exit(1)),g.start("Generating feature files..."),await dr({name:s.featureName,outputPath:a,createStore:s.createStore,persistStore:s.persistStore,createApi:s.createApi,state:o.state,hasI18n:o.hasI18n,hasTests:o.hasTests}),g.succeed("Feature files generated"),o.hasI18n&&await Se(n,$(s.featureName)),s.createApi&&(g.start("Registering API endpoints..."),await ct({serviceName:s.featureName,projectPath:n}),g.succeed("API endpoints registered")),s.createStore&&o.state==="redux"&&(g.start("Registering slice in the store..."),await pt({projectPath:n,name:s.featureName,persist:s.persistStore,importPath:_r.join(i,s.featureName,"store")}),g.succeed("Slice registered")),$c(s,i,o.state)}catch(n){g.fail("Feature generation failed"),x(`${n instanceof Error?n.message:n}`),process.exit(1)}})},$c=(e,t,r)=>{let n=_r.join(t,e.featureName);l(_.green(`
633
+ \u2728 Feature '${e.featureName}' created.
634
+ `)),l(_.dim(` ${n}/`)),e.createApi&&l(_.dim(" api/ schema, keys, server (DAL), actions, queries")),l(_.dim(" components/")),e.createApi||l(_.dim(" hooks/")),e.createStore&&l(_.dim(" store/")),l(_.dim(" types/")),l(_.dim(" index.ts client-safe barrel")),e.createApi&&l(_.dim(" server.ts server-only barrel")),l(""),l(_.cyan("Next:")),e.createApi&&(l(_.dim(` 1. Adjust the contracts in ${n}/api/schema.ts and the paths in src/lib/config/app-apis.ts`)),l(_.dim(` 2. Read in a Server Component: import { list${Jo(e.featureName)}s } from '@/features/${e.featureName}/server'`)),l(_.dim(` 3. Prefetch + hydrate for the client list: prefetchQuery(${Pc(e.featureName)}ListQuery())`))),e.createStore&&r==="zustand"&&l(_.yellow(` ! Compose create${Jo(e.featureName)}Slice into AppState in src/store/index.ts`)),l("")},Pc=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),Jo=$;import Dr from"node:path";import Be from"picocolors";import kc from"enquirer";var{prompt:Sc}=kc,Wo=async e=>e?{hookName:e}:await Sc({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 Ho=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();l(Be.cyan(`
635
+ \u{1FA9D} Hook Generator
636
+ `));let o=await T(n),s=await Wo(t);F(s.hookName,"hook name"),r.feature&&W(r.feature,"feature path");let a=`use${$(s.hookName)}`,p=Ec(r,o.hasTests);g.start("Generating hook..."),await fr({name:s.hookName,projectPath:n,isClient:r.client??!0,featurePath:r.feature}),g.succeed("Hook generated");let m=r.feature?Dr.join(n,r.feature,"hooks",`${a}.ts`):Dr.join(n,"src/hooks",`${a}.ts`),c=null;p&&(g.start("Generating test..."),c=await re({projectPath:n,sourceFile:m,kind:"hook",symbolName:a,hookUsesStore:o.state!=="none"&&!!r.feature}),g.succeed("Test generated"));let d=r.feature?`${r.feature}/hooks/${a}.ts`:`src/hooks/${a}.ts`;l(Be.green(`
637
+ \u2728 Hook '${a}' created successfully!
638
+ `)),l(Be.dim(` \u{1F4C4} ${d}`)),c&&l(Be.dim(` \u{1F9EA} ${Dr.relative(n,c)}`)),l(""),l(Be.cyan("Usage:"));let u=r.feature?`${r.feature.replace(/^src\//,"@/")}/hooks/${a}`:`@/hooks/${a}`;l(Be.dim(` import { ${a} } from '${u}';`)),l("")}catch(n){g.fail("Hook generation failed"),x(`${n}`),process.exit(1)}})},Ec=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import Ut from"picocolors";var Ko=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();l(Ut.cyan(`
639
+ \u{1F4D0} Layout Generator
640
+ `)),g.start("Detecting project setup...");let o=await T(n);g.succeed("Project setup detected");let s=o.hasI18n&&r.locale!==!1;l(Ut.dim(` i18n: ${s?"\u2713":"\u2717"}
641
+ `)),g.start("Generating layout...");let i=await gr({segment:t,at:r.at,projectPath:n,hasI18n:s,withGroup:!!r.group});g.succeed("Layout generated"),l(Ut.green(`
642
+ \u2728 ${i.componentName} created!
643
+ `)),l(Ut.dim(` \u{1F4C4} ${i.displayPath}`)),l("")}catch(n){g.fail("Layout generation failed"),x(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Y from"picocolors";import Rc from"enquirer";var{prompt:jc}=Rc,Xo=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 jc(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 Yo=e=>{e.command("locale [code]").description("Add a new locale/language").option("--copy-translations","Copy translations from English instead of 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();l(Y.cyan(`
644
+ \u{1F30D} Locale Generator
645
+ `)),g.start("Detecting project setup...");let o=await T(n);g.succeed("Project setup detected"),o.hasI18n||(g.fail("i18n is not set up in this project"),x("Please set up internationalization first"),l(Y.dim(`
646
+ Run: npx @teispace/next-maker setup --i18n
647
+ `)),process.exit(1));let s=await Xo(t,{copyTranslations:r.copyTranslations,name:r.name,country:r.country,flag:r.flag});F(s.code,"locale code");let i=te(n,"src","i18n","translations",`${s.code}.json`);y(i)&&(x(`Locale '${s.code}' already exists!`),process.exit(1)),g.start("Adding locale..."),await hr({...s,projectPath:n}),g.succeed("Locale added"),l(Y.green(`
648
+ \u2728 Locale '${s.code}' (${s.name}) added successfully!
649
+ `)),l(Y.dim("Updated files:")),l(Y.dim(` \u{1F4C4} src/i18n/translations/${s.code}.json`)),l(Y.dim(" \u{1F4DD} src/types/i18n.ts (SupportedLocale type)")),l(Y.dim(" \u{1F4DD} src/lib/config/app-locales.ts")),l(""),l(Y.cyan("Next steps:")),l(Y.dim(` 1. Translate: src/i18n/translations/${s.code}.json`)),l(Y.dim(` 2. Visit: http://localhost:3000/${s.code}`)),l("")}catch(n){g.fail("Locale generation failed"),x(`${n}`),process.exit(1)}})};import Cc from"node:path";import mt from"picocolors";var Qo=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=y(Cc.join(n,".next-maker.json")),s=o?await ee(n):{cli:"",starter:{name:"",version:"",source:""},packageManager:"pnpm",answers:{}},i=await Me(s,{starterPath:t.starterPath});r=i.dispose,l(mt.cyan(`
650
+ ${i.manifest.starter.name} ${i.manifest.starter.version}
651
+ `));for(let[a,p]of Object.entries(i.manifest.options)){let m=o?s.answers[a]:p.default,c=p.type==="boolean"?"true | false":(p.values??[]).join(" | ");l(` ${mt.bold(a.padEnd(18))} ${String(Array.isArray(m)?m.join(",")||"none":m).padEnd(10)} ${mt.dim(c)}`),p.label&&l(` ${"".padEnd(18)} ${mt.dim(p.label)}`)}l(""),l(mt.dim(o?"Change with: next-maker setup --set <option>=<value>":"Use with: next-maker init --set <option>=<value>")),l("")}catch(n){x(`${n instanceof Error?n.message:n}`),process.exit(1)}finally{await r?.()}})};import Lr from"node:path";import V from"picocolors";import Tc from"enquirer";var{prompt:Oc}=Tc,Zo=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 Oc(r):{};return{pageName:e??n.pageName,withLoading:t.loading??n.withLoading??!0,withError:t.error??n.withError??!0}};var es=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();l(V.cyan(`
652
+ \u{1F4C4} Page Generator
653
+ `)),g.start("Detecting project setup...");let o=await T(n);g.succeed("Project setup detected"),l(V.dim(` i18n: ${o.hasI18n?"\u2713":"\u2717"}
654
+ `));let s=await Zo(t,{loading:r.loading,error:r.error}),i=s.pageName;F(i,"page name"),r.dynamic&&F(r.dynamic,"dynamic param"),r.group&&F(r.group,"route group");let a=$(i),p=Lr.join(o.hasI18n?"src/app/[locale]":"src/app",r.group?`(${r.group})`:"");await ve(n,i,p)&&(x(`Page '${i}' already exists at ${p}/${i}!`),process.exit(1)),g.start("Generating page files..."),await wr({name:i,projectPath:n,hasI18n:o.hasI18n,group:r.group,dynamic:r.dynamic,withLoading:r.loading??s.withLoading,withError:r.error??s.withError}),g.succeed("Page files generated"),g.start("Registering route...");let c=r.dynamic?`/${i}/[${r.dynamic}]`:`/${i}`;await kr(n,i,c),g.succeed("Route registered"),o.hasI18n&&(g.start("Adding translation namespace..."),await Se(n,a),g.succeed("Translation namespace added"));let d=r.dynamic?Lr.join(p,i,`[${r.dynamic}]`):Lr.join(p,i);l(V.green(`
655
+ \u2728 Page '${i}' created successfully!
656
+ `)),l(V.dim("Generated files:")),l(V.dim(` \u{1F4C2} ${d}/`)),l(V.dim(" \u251C\u2500\u2500 page.tsx")),(r.loading??s.withLoading)&&l(V.dim(" \u251C\u2500\u2500 loading.tsx")),(r.error??s.withError)&&l(V.dim(" \u2514\u2500\u2500 error.tsx")),l(""),o.hasI18n&&(l(V.cyan("Next steps:")),l(V.dim(` 1. Add translations in: src/i18n/translations/en.json \u2192 "${a}"`)),l(V.dim(` 2. Visit: http://localhost:3000/${i}`)),l(""))}catch(n){g.fail("Page generation failed"),x(`${n}`),process.exit(1)}})};import Ur from"node:path";import ue from"picocolors";var ts=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();l(ue.cyan(`
657
+ \u{1F50C} Provider Generator
658
+ `)),g.start("Generating provider...");let{componentName:n,fileBaseName:o,providerFile:s}=await br({name:t,projectPath:r});g.succeed("Provider generated"),g.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:i,barrelFile:a}=await jr({projectPath:r,componentName:n,fileBaseName:o});g.succeed("Provider registered"),l(ue.green(`
659
+ \u2728 ${n} created!
660
+ `)),l(ue.dim("Generated files:")),l(ue.dim(` \u{1F4C4} ${Ur.relative(r,s)}`)),i?l(ue.dim(` \u270F\uFE0F ${Ur.relative(r,i)} \u2014 wrapped {children}`)):(l(ue.yellow(`
661
+ \u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),l(ue.dim(` <${n}>{children}</${n}>`))),a&&l(ue.dim(` \u270F\uFE0F ${Ur.relative(r,a)} \u2014 added re-export`)),l("")}catch(r){g.fail("Provider generation failed"),x(`${r instanceof Error?r.message:r}`),process.exit(1)}})};import Ac from"picocolors";var rs=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){x(`${n instanceof Error?n.message:n}`),process.exit(1)}}).addHelpText("after",`
662
+ ${Ac.dim("Example: next-maker remove ws")}
663
+ `)};import Gr from"node:path";import Br from"picocolors";var ns=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();if(l(Br.cyan(`
664
+ \u{1F50C} API Layer Generator
665
+ `)),!t)throw new Error("Pass the resource name, e.g. `next-maker api invoice`.");F(t,"resource name"),r.feature&&W(r.feature,"feature path");let o=await T(n);if(!o.hasQuery||!o.hasActions)throw new Error("This generator needs @tanstack/react-query and next-safe-action (starter 2.x).");let s=r.feature?te(n,r.feature):te(n,"src","features",t);if(y(Gr.join(s,"api","schema.ts")))throw new Error(`${Gr.relative(n,s)}/api already exists.`);g.start("Generating api/ files...");let i=await nt({name:t,featurePath:s,withActions:r.actions!==!1});g.succeed("api/ generated"),g.start("Registering API endpoints..."),await ct({serviceName:t,projectPath:n}),g.succeed("Endpoints registered in src/lib/config/app-apis.ts"),l(Br.green(`
666
+ \u2728 ${Gr.relative(n,s)}/api ready.
667
+ `));for(let a of i)l(Br.dim(` ${a}`));l("")}catch(n){g.fail("API generation failed"),x(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Xc from"enquirer";import q from"picocolors";import{execFile as Ic}from"node:child_process";import{cp as gh,mkdir as is,readFile as Nc,rm as as,stat as hh,writeFile as Gt}from"node:fs/promises";import Q from"node:path";import{promisify as Fc}from"node:util";var Mc=Fc(Ic),_c=new Set(["pnpm-lock.yaml","package-lock.json","yarn.lock","bun.lock","bun.lockb",".env",".next-maker.json","next-env.d.ts","tsconfig.tsbuildinfo"]),Dc=e=>_c.has(e)||e.startsWith(".env.")||e.endsWith(".log"),Lc=new Set([".png",".jpg",".jpeg",".gif",".webp",".avif",".ico",".woff",".woff2",".ttf",".otf",".pdf",".zip",".gz"]),Vr=(e,t)=>Lc.has(Q.extname(e))||t.subarray(0,8e3).includes(0),qr=async e=>{try{return await Nc(e)}catch{return null}},dt=async(e,t,r)=>{r||(await is(Q.dirname(e),{recursive:!0}),await Gt(e,t))},os=async(e,t,r,n={ours:"project",base:"starter (previous)",theirs:"starter (new)"})=>{let o=Q.join(process.env.TMPDIR??"/tmp",`next-maker-merge-${process.pid}-${Math.random().toString(36).slice(2)}`);await is(o,{recursive:!0});try{await Gt(Q.join(o,"ours"),e),await Gt(Q.join(o,"base"),t),await Gt(Q.join(o,"theirs"),r);try{let{stdout:s}=await Mc("git",["merge-file","-p","-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{content:i.stdout,conflicts:i.code};throw i.code==="ENOENT"?new Error("git is required for merges but was not found on PATH."):s}}finally{await as(o,{recursive:!0,force:!0})}},ss=["scripts","dependencies","devDependencies","peerDependencies","optionalDependencies","engines"],Uc=(e,t,r)=>{let n=structuredClone(e),o=[],s=(a,p)=>JSON.stringify(a)===JSON.stringify(p),i=(a,p,m,c,d,u)=>{let f=m[d],h=c[d],w=p[d];if(!s(f,h)){if(s(w,f)||s(w,h)){h===void 0?delete a[d]:a[d]=h;return}o.push(u)}};for(let a of ss){let p=t[a]??{},m=r[a]??{},c=e[a]??{};n[a]||(n[a]={});let d=n[a];for(let u of new Set([...Object.keys(p),...Object.keys(m)]))i(d,c,p,m,u,`${a}.${u}`);Object.keys(d).length===0&&!(a in r)?delete n[a]:a!=="scripts"&&a!=="engines"&&(n[a]=Object.fromEntries(Object.entries(d).sort(([u],[f])=>u.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}},Ve=async({base:e,theirs:t,ours:r},n={})=>{let o=n.dryRun??!1,s=n.skip??Dc,[i,a,p]=await Promise.all([L(e),L(t),L(r)]),m=new Set(p),c=new Set(i),d=new Set(a),u={entries:[],conflicts:[]},f=(h,w,P)=>{u.entries.push({file:h,outcome:w,note:P}),w==="conflict"&&u.conflicts.push(h)};for(let h of[...new Set([...i,...a])].sort()){if(s(h))continue;let w=c.has(h),P=d.has(h),b=m.has(h),[S,R,D]=await Promise.all([w?qr(Q.join(e,h)):null,P?qr(Q.join(t,h)):null,b?qr(Q.join(r,h)):null]),Ce=Q.join(r,h);if(P&&!w){if(!b)await dt(Ce,R,o),f(h,"added");else if(D.equals(R))f(h,"unchanged");else if(Vr(h,R))f(h,"kept","starter added a binary the project also has");else{let ut=await os(D.toString("utf8"),"",R.toString("utf8"));await dt(Ce,ut.content,o),f(h,ut.conflicts?"conflict":"merged")}continue}if(w&&!P){if(!b)continue;D.equals(S)?(o||await as(Ce,{force:!0}),f(h,"deleted")):f(h,"kept","starter removed it, but the project changed it");continue}if(!b){f(h,"kept","deleted in the project");continue}if(S.equals(R)||D.equals(R)){f(h,"unchanged");continue}if(D.equals(S)){await dt(Ce,R,o),f(h,"updated");continue}if(Vr(h,D)||Vr(h,R)){f(h,"kept","binary changed on both sides");continue}if(h==="package.json"){let{result:ut,conflicts:ft}=Uc(JSON.parse(D.toString("utf8")),JSON.parse(S.toString("utf8")),JSON.parse(R.toString("utf8")));await dt(Ce,`${JSON.stringify(ut,null,2)}
668
+ `,o),f(h,ft.length?"conflict":"merged",ft.length?`project kept: ${ft.join(", ")}`:void 0),ft.length&&u.conflicts.pop();continue}let Kr=await os(D.toString("utf8"),S.toString("utf8"),R.toString("utf8"));await dt(Ce,Kr.content,o),f(h,Kr.conflicts?"conflict":"merged")}return u};import{execFile as Gc}from"node:child_process";import{mkdtemp as Bc,readFile as Vc,rm as cs}from"node:fs/promises";import{tmpdir as qc}from"node:os";import oe from"node:path";import{promisify as Jc}from"node:util";var zc=Jc(Gc),Bt=async(e,t)=>{if(t.identity)return le(t.identity.projectName,{...t.identity,copyEnv:!1});let r=JSON.parse(await Vc(oe.join(e,"package.json"),"utf-8"));return le(oe.basename(e),{projectName:r.name??oe.basename(e),description:r.description,author:typeof r.author=="string"?r.author:void 0,version:r.version,readme:y(oe.join(e,"README.md")),copyEnv:!1})},ps=e=>{let t=e.starter.source;if(t.startsWith("/")||t.startsWith(".")){if(!y(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:oe.resolve(t)}}return{kind:"remote",location:t}},Jr=e=>e.startsWith("/")||e.startsWith(".")||y(e)?{kind:"local",location:oe.resolve(e)}:B({ref:e.includes("#")?e.split("#")[1]:e,starterPath:void 0}),qe=async(e,t,r,n,o)=>{let s=await Bc(oe.join(qc(),"next-maker-ref-")),i=oe.join(s,r.projectName);try{let a=await xe({projectPath:i,source:e,supplied:t,packageManager:n});await be(i,r,a,e);let p=o?oe.join(o,"node_modules",".bin","biome"):void 0;if(p&&y(p))try{await zc(p,["check","--write","--no-errors-on-unmatched","."],{cwd:i,maxBuffer:64*1024*1024})}catch{}return{dir:i,scaffold:a,dispose:()=>cs(s,{recursive:!0,force:!0})}}catch(a){throw await cs(s,{recursive:!0,force:!0}),a}};import Wc from"enquirer";import E from"picocolors";var{prompt:Hc}=Wc,Kc={added:E.green("+"),updated:E.cyan("\u2191"),merged:E.cyan("\u21C4"),deleted:E.red("-"),conflict:E.red("\u2717"),kept:E.yellow("\xB7"),skipped:E.dim("\xB7")},zr=e=>{let t=e.entries.filter(n=>n.outcome!=="unchanged");if(t.length===0){l(E.dim(" no file changes"));return}for(let n of t)l(` ${Kc[n.outcome]??" "} ${n.outcome.padEnd(8)} ${n.file}${n.note?E.dim(` (${n.note})`):""}`);let r={};for(let n of t)r[n.outcome]=(r[n.outcome]??0)+1;if(l(""),l(E.dim(` ${Object.entries(r).map(([n,o])=>`${o} ${n}`).join(", ")}`)),e.conflicts.length){l(""),l(E.red(` ${e.conflicts.length} file(s) need attention; look for <<<<<<< markers:`));for(let n of e.conflicts)l(E.red(` ${n}`))}},ls=e=>{e.command("upgrade").description(`Merge starter changes into this project (default target: ${Pt})`).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 ee(r),s=await Bt(r,o),i=t.from?Jr(t.from):ps(o),a=t.to?Jr(t.to):B({starterPath:t.starterPath,ref:Pt});if(i.location===a.location){l(E.dim(`Already on ${a.location}; nothing to upgrade.`));return}l(E.cyan(`
669
+ \u2B06 Upgrade
670
+ `)),l(` ${E.dim("from")} ${i.location}`),l(` ${E.dim("to ")} ${a.location}
671
+ `);let p=await qe(i,o.answers,s,o.packageManager,r);n.push(p.dispose);let m=await qe(a,o.answers,s,o.packageManager,r);n.push(m.dispose);for(let u of m.scaffold.forced)l(E.yellow(` ! ${u.reason}; ${u.option} turned off`));for(let u of m.scaffold.unknown)l(E.yellow(` ! option "${u}" no longer exists in the new starter`));let c=await Ve({base:p.dir,theirs:m.dir,ours:r},{dryRun:!0});if(zr(c),t.dryRun){l(E.dim(`
672
+ Dry run: nothing was written.
673
+ `));return}if(!t.yes){let{confirm:u}=await Hc({type:"confirm",name:"confirm",message:"Apply these changes?",initial:!1});if(!u){l(E.yellow(`Aborted.
674
+ `));return}}let d=await Ve({base:p.dir,theirs:m.dir,ours:r});await me(r,{...o,cli:Te.version,starter:{...m.scaffold.manifest.starter,source:a.location},answers:m.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&&(l(E.dim(`
675
+ Installing with ${o.packageManager}...`)),await pe(r,o.packageManager)),l(E.green(`
676
+ \u2713 Upgraded to ${m.scaffold.manifest.starter.version}.`)),d.conflicts.length?l(E.yellow(" Resolve the conflicts above, then run `next-maker doctor --compile`.")):l(E.dim(" Run `next-maker doctor --compile` to confirm.")),l("")}catch(o){x(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{for(let o of n)await o()}})};var{prompt:Yc}=Xc,Qc=(e,t=[])=>[...t,e],ms=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",Qc).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 ee(r),s=ye(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 Me(o,{starterPath:t.starterPath});n.push(i.dispose);let{answers:a,forced:p,unknown:m}=Ke(i.manifest,{...o.answers,...s});for(let P of m)l(q.yellow(` ! unknown option "${P}" ignored`));for(let P of p)l(q.yellow(` ! ${P.reason}; ${P.option} turned off`));let c=Object.keys(a).filter(P=>JSON.stringify(a[P])!==JSON.stringify(o.answers[P]));if(l(q.cyan(`
677
+ \u{1F527} Setup
678
+ `)),c.length===0){l(q.dim(`No option changes; the project already has these values.
679
+ `));return}for(let P of c)l(` ${q.dim(P.padEnd(18))} ${String(o.answers[P])} ${q.dim("\u2192")} ${String(a[P])}`);l("");let d=await Bt(r,o),u=await qe(i.source,o.answers,d,o.packageManager,r);n.push(u.dispose);let f=await qe(i.source,a,d,o.packageManager,r);n.push(f.dispose);let h=await Ve({base:u.dir,theirs:f.dir,ours:r},{dryRun:!0});if(zr(h),t.dryRun){l(q.dim(`
680
+ Dry run: nothing was written.
681
+ `));return}if(!t.yes){let{confirm:P}=await Yc({type:"confirm",name:"confirm",message:"Apply these changes?",initial:!1});if(!P){l(q.yellow(`Aborted.
682
+ `));return}}let w=await Ve({base:u.dir,theirs:f.dir,ours:r});await me(r,{...o,answers:a}),t.install!==!1&&(l(q.dim(`
683
+ Installing with ${o.packageManager}...`)),await pe(r,o.packageManager)),l(q.green(`
684
+ \u2713 Applied.`)),w.conflicts.length&&l(q.yellow(" Resolve the conflicts above, then run `next-maker doctor --compile`.")),l("")}catch(o){x(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{for(let o of n)await o()}})};import Wr from"node:path";import Vt from"picocolors";import Zc from"enquirer";var{prompt:ep}=Zc,ds=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 ep(r):{};return{sliceName:e||n.sliceName,persistSlice:t===void 0?n.persistSlice||!1:t}};var us=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();l(Vt.cyan(`
685
+ \u{1F527} Slice Generator
686
+ `)),g.start("Detecting project setup...");let o=await T(n);if(g.succeed("Project setup detected"),o.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`.");l(Vt.dim(` state: ${o.state}
687
+ `));let s=await ds(t,o.state==="zustand"?!1:r.persist);F(s.sliceName,"slice name"),r.path&&W(r.path,"path");let i=r.path??Wr.join("src","store","slices"),a=te(n,i,s.sliceName);if(await ve(n,s.sliceName,i))throw new Error(`Slice '${s.sliceName}' already exists at ${i}.`);g.start("Generating slice files..."),await ot({name:s.sliceName,outputPath:a,persist:s.persistSlice,state:o.state}),g.succeed("Slice files generated"),o.state==="redux"&&(g.start("Registering slice in the store..."),await pt({projectPath:n,name:s.sliceName,persist:s.persistSlice,importPath:Wr.join(i,s.sliceName)}),g.succeed("Slice registered"),(r.test??o.hasTests)&&(g.start("Generating test..."),await re({projectPath:n,sourceFile:Wr.join(a,`${s.sliceName}.slice.ts`),kind:"slice",symbolName:s.sliceName.replace(/-([a-z])/g,(p,m)=>m.toUpperCase())}),g.succeed("Test generated"))),l(Vt.green(`
688
+ \u2728 Slice '${s.sliceName}' created in ${i}/${s.sliceName}/.
689
+ `)),o.state==="zustand"&&(l(Vt.yellow(` ! Compose create${$(s.sliceName)}Slice into AppState in src/store/index.ts`)),l(""))}catch(n){g.fail("Slice generation failed"),x(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import J from"node:path";import Hr from"picocolors";var fs=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
690
+ 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=J.isAbsolute(t)?t:J.join(n,t);l(Hr.cyan(`
691
+ \u{1F9EA} Test Generator
692
+ `)),y(o)||(x(`Source file not found: ${t}`),process.exit(1));let s=await T(n);s.hasTests||(x("Testing is not installed in this project. Run `next-maker setup --tests` first."),process.exit(1));let i=await j(o),a=tp(r.kind)??rp(o,i);a||(x(`Could not infer test kind for ${J.basename(o)}. Pass --kind component|hook|slice.`),process.exit(1));let p=np(o,a),m=op(o,a);y(m)&&!r.force&&(x(`Test file already exists: ${J.relative(n,m)}. Use --force to overwrite.`),process.exit(1)),g.start(`Generating ${a} test...`);let c=await re({projectPath:n,sourceFile:o,kind:a,symbolName:p,hasState:s.state!=="none",hasI18n:s.hasI18n,hookUsesStore:a==="hook"&&s.state!=="none"&&i.includes("useAppSelector")});g.succeed("Test generated"),l(Hr.green(`
693
+ \u2728 Test '${J.basename(c)}' created.
694
+ `)),l(Hr.dim(` \u{1F9EA} ${J.relative(n,c)}`)),l("")}catch(n){g.fail("Test generation failed"),x(`${n}`),process.exit(1)}})},tp=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},rp=(e,t)=>{let r=J.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},np=(e,t)=>{let r=J.basename(e).replace(/\.(tsx?|jsx?)$/i,"");return t==="slice"?U(r.replace(/\.slice$/,"")):t==="hook"?/^use[A-Z]/.test(r)?r:`use${$(r.replace(/^use-?/,""))}`:$(r)},op=(e,t)=>{let r=J.dirname(e),n=J.basename(e).replace(/\.(tsx?|jsx?)$/i,""),o=t==="component"?".tsx":".ts";return J.join(r,`${n}.test${o}`)};import{chmod as sp,cp as ip,mkdir as qt,readFile as Jt,rm as ap,writeFile as I}from"node:fs/promises";import v from"node:path";import fe from"picocolors";var gs=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)}
695
+ `},hs=e=>`packages:
696
+ - 'apps/*'
697
+ - 'packages/*'
698
+
699
+ ${e.split(`
700
+ `).filter(r=>!r.startsWith("packages:")&&!r.startsWith("# Single-package")&&!r.startsWith("# Monorepo mode")).join(`
862
701
  `).trim()}
863
- `,await h(s,d))}let c=T.join(e,a.ERRORS_DIR);if(l(c)){let d=await re.readdir(c),f=!1;for(let x of d){let I=T.parse(x).name;if(await t(e,I,[T.join(c,x)])){f=!0;break}}f||await v(c)}let p=T.join(e,a.COMMON_TYPES_DIR);l(p)&&(await re.readdir(p)).length===0&&await v(p);let u=T.join(e,a.UTILITY_TYPES_DIR);l(u)&&(await re.readdir(u)).length===0&&await v(u)};P();w();import wt from"node:path";var rr=async e=>{let t=wt.join(e,a.FETCH_CLIENT_FILE),r=wt.join(e,a.AXIOS_CLIENT_FILE),i=l(t),n=l(r);return i&&n?{status:"both",reason:"Both Fetch and Axios clients are detected"}:i?{status:"fetch",reason:"Fetch client is already setup"}:n?{status:"axios",reason:"Axios client is already setup"}:{status:"none"}},_o=async e=>{let r=[wt.join(e,"src/lib"),wt.join(e,"src/types"),wt.join(e,"src/lib/config")].filter(i=>!l(i));if(r.length>0)throw new Error(`Invalid project structure. Missing directories:
864
- ${r.map(i=>`- ${i}`).join(`
865
- `)}`)};Be();var or=async e=>{let t=S("Checking HTTP Client setup..."),r=ir.join(e,".next-maker-temp-http");try{let{status:i,reason:n}=await rr(e);t.stop();let o=await rl(i,n);if(o.type==="cancel"){console.log(ai.yellow("Setup cancelled."));return}if(t.start("Setting up HTTP client(s)..."),await _o(e),await ko(r,t),o.type==="replace"&&o.fromClient&&(t.text="Migrating client usages...",await Dr(e,o.fromClient,o.clients[0])),o.type!=="remove-both"&&o.type!=="remove-one"){t.text="Copying client files...";let I=o.type==="replace"&&o.fromClient?[o.fromClient]:void 0;await No(e,r,o.clients,I)}if(o.type==="remove-one"||o.type==="remove-both"){t.text="Removing client files...";let{PROJECT_PATHS:I}=await Promise.resolve().then(()=>(P(),qi));for(let k of o.clients){let Z=ir.join(e,k==="fetch"?I.FETCH_CLIENT:I.AXIOS_CLIENT);await v(Z)}Fo(i,o).length===0&&(t.text="Performing full cleanup...",await Ao(e),await Lr(e))}let s=Fo(i,o);s.length>0?(t.text="Configuring client...",await Nr(e,s),await Ar(e),await _r(e),await Fr(e),await tt(e),await we(e,s),await Pe(e,s)):await rt(e),await Ue(e,s),t.text="Managing dependencies...";let c=ir.join(e,"package.json"),p=JSON.parse(await tl.readFile(c,"utf-8")),u={...p.dependencies,...p.devDependencies};s.includes("axios")?u.axios||(t.text="Installing Axios...",await z(e,"axios")):u.axios&&(t.text="Uninstalling Axios...",await He(e,"axios")),s.length>0?u["server-only"]||(t.text="Installing server-only...",await z(e,"server-only")):u["server-only"]&&(t.text="Uninstalling server-only...",await He(e,"server-only"));let d=ir.join(e,"src/services/storage");s.length>0||l(d)?u["react-secure-storage"]||(t.text="Installing react-secure-storage...",await z(e,"react-secure-storage")):u["react-secure-storage"]&&(t.text="Uninstalling react-secure-storage...",await He(e,"react-secure-storage")),t.text="Formatting code...";let x=await A(e);await K(e,x,"lint:fix"),t.succeed(ai.green(il(o)))}catch(i){throw t.fail("Failed to setup HTTP Client."),i}finally{await v(r)}};async function rl(e,t){if(e==="none"){let{choice:i}=await nr.prompt({type:"select",name:"choice",message:"Which HTTP client(s) do you want to setup?",choices:[{name:"fetch",message:"Fetch (Native)"},{name:"axios",message:"Axios"},{name:"both",message:"Both (Fetch + Axios)"}]});return i==="both"?{type:"install",clients:["fetch","axios"]}:{type:"install",clients:[i]}}if(e==="fetch"){let{action:i}=await nr.prompt({type:"select",name:"action",message:`${t}. What would you like to do?`,choices:[{name:"cancel",message:"Keep Fetch only (cancel)"},{name:"add",message:"Add Axios (have both)"},{name:"replace",message:"Replace with Axios only"},{name:"remove",message:"Remove Fetch"}]});if(i==="cancel")return{type:"cancel",clients:[]};if(i==="add")return{type:"add",clients:["axios"]};if(i==="replace")return{type:"replace",clients:["axios"],fromClient:"fetch"};if(i==="remove")return{type:"remove-one",clients:["fetch"]}}if(e==="axios"){let{action:i}=await nr.prompt({type:"select",name:"action",message:`${t}. What would you like to do?`,choices:[{name:"cancel",message:"Keep Axios only (cancel)"},{name:"add",message:"Add Fetch (have both)"},{name:"replace",message:"Replace with Fetch only"},{name:"remove",message:"Remove Axios"}]});if(i==="cancel")return{type:"cancel",clients:[]};if(i==="add")return{type:"add",clients:["fetch"]};if(i==="replace")return{type:"replace",clients:["fetch"],fromClient:"axios"};if(i==="remove")return{type:"remove-one",clients:["axios"]}}console.log(ai.yellow(`\u2716 ${t}`));let{action:r}=await nr.prompt({type:"select",name:"action",message:"What would you like to do?",choices:[{name:"cancel",message:"Keep both (cancel)"},{name:"remove-fetch",message:"Remove Fetch (keep Axios)"},{name:"remove-axios",message:"Remove Axios (keep Fetch)"},{name:"remove-both",message:"Remove both"}]});return r==="cancel"?{type:"cancel",clients:[]}:r==="remove-fetch"?{type:"remove-one",clients:["fetch"]}:r==="remove-axios"?{type:"remove-one",clients:["axios"]}:r==="remove-both"?{type:"remove-both",clients:["fetch","axios"]}:{type:"cancel",clients:[]}}function Fo(e,t){return t.type==="install"?t.clients:t.type==="add"?[...e==="fetch"?["fetch"]:["axios"],...t.clients]:t.type==="replace"?t.clients:t.type==="remove-one"?e==="both"?t.clients[0]==="fetch"?["axios"]:["fetch"]:[]:[]}function il(e){if(e.type==="install")return`HTTP Client (${e.clients.map(r=>r==="fetch"?"Fetch":"Axios").join(" + ")}) setup successfully!`;if(e.type==="add")return`${e.clients[0]==="fetch"?"Fetch":"Axios"} client added successfully! Both clients are now available.`;if(e.type==="replace"){let t=e.fromClient==="fetch"?"Fetch":"Axios",r=e.clients[0]==="fetch"?"Fetch":"Axios";return`Replaced ${t} with ${r} successfully! Imports automatically migrated.`}return e.type==="remove-one"?`${e.clients[0]==="fetch"?"Fetch":"Axios"} client removed successfully!`:e.type==="remove-both"?"Both HTTP clients removed successfully!":"HTTP Client setup completed!"}Be();P();import nl from"node:fs/promises";import Ae from"node:path";import ol from"picocolors";w();Be();var Oo=async(e,t)=>{let r=S("Repairing HTTP Client..."),i=Ae.join(e,".next-maker-temp-http-repair");try{let{status:n}=await rr(e),o=n==="both"?["fetch","axios"]:n==="none"?[]:[n],s=[a.HTTP_SHARED,a.HTTP_BUNDLE_SENTINEL_DIR].filter(f=>F(t,f)&&!l(Ae.join(e,f))),c=[a.HTTP_SERVER_FILE,a.API_URL_FILE].filter(f=>F(t,f)&&!l(Ae.join(e,f))),p=[];(s.length>0||c.length>0)&&(r.text="Fetching assets from starter repo...",await te(i,async f=>{for(let x of s)await nl.cp(Ae.join(f,x),Ae.join(e,x),{recursive:!0,force:!0}),p.push(x);for(let x of c)await b(Ae.join(f,x),Ae.join(e,x)),p.push(x)}),o.length>0&&(await we(e,o),await Pe(e,o))),U(t)&&(r.text="Re-mounting the bundle sentinel...",await tt(e),p.push("<HttpClientBundleSentinel /> mount"));let u=await B(e,t),d=[p.length>0?`restored ${p.join(", ")}`:null,u.length>0?`reinstalled ${u.join(", ")}`:null].filter(Boolean);r.succeed(ol.green(d.length>0?`HTTP Client repaired (${d.join("; ")}).`:"HTTP Client repaired."))}catch(n){throw r.fail("Failed to repair HTTP Client."),n}};var sl=/<HttpClientBundleSentinel\s*\/>/,Do={id:"http-client",name:"HTTP Client",description:"Axios and/or Fetch client utilities",detect:async e=>{let{detectProjectSetup:t}=await Promise.resolve().then(()=>(q(),ct));return(await t(e)).httpClient!=="none"},files:[{path:"src/lib/utils/http",generated:!0,isDir:!0,containsUserContent:!0,removeHint:"holds the http client + any service modules you wired through it \u2014 review before deleting"},{path:"src/lib/utils/http/shared",generated:!0,isDir:!0},{path:"src/lib/utils/http/server.ts",generated:!0},{path:"src/lib/utils/http/__bundle-sentinel__",generated:!0,isDir:!0},{path:"src/lib/config/api-url.ts",generated:!0}],packages:[],scripts:[],injections:[{file:"src/app/[locale]/layout.tsx",alternativeFiles:["src/app/layout.tsx"],description:"<HttpClientBundleSentinel /> mount in the app layout",presence:sl,removePattern:_t}],apply:O(or,Oo)};P();import ci from"node:path";import pl from"picocolors";w();P();import al from"node:fs/promises";import Te from"node:path";w();var jo=async(e,t)=>{t.text="Fetching assets from starter repo...",await J(e)},Lo=async(e,t)=>{let r=Te.join(t,"src/i18n"),i=Te.join(e,a.I18N_DIR);await al.cp(r,i,{recursive:!0});let n=Te.join(t,"src/proxy.ts"),o=Te.join(e,a.PROXY);await b(n,o);let s=Te.join(t,"src/types/i18n.ts"),c=Te.join(e,a.I18N_TYPES);await b(s,c);let p=Te.join(t,"src/lib/config/app-locales.ts"),u=Te.join(e,a.APP_LOCALES);await b(p,u)};P();w();import sr from"node:path";var Mo=async e=>{let t=sr.join(e,a.I18N_DIR),r=sr.join(e,"package.json");if(l(t))return{isSetup:!0,reason:"src/i18n directory exists"};if(l(r)){let i=JSON.parse(await g(r));if(i.dependencies?.["next-intl"]||i.devDependencies?.["next-intl"])return{isSetup:!0,reason:"next-intl is installed"}}return{isSetup:!1,reason:""}},Go=async e=>{let t=sr.join(e,a.ROOT_LAYOUT),r=sr.join(e,"src/app/[locale]/layout.tsx");if(!l(t)&&!l(r))throw new Error("Project structure mismatch. Ensure src/app/layout.tsx exists.")};P();w();import cl from"node:fs/promises";import he from"node:path";var ar=async e=>{let t=he.join(e,a.NEXT_CONFIG);if(l(t)){let r=await g(t);if(!r.includes("createNextIntlPlugin")){r=`import createNextIntlPlugin from 'next-intl/plugin';
866
- ${r}`;let i=/export default (.*?);/;i.test(r)?r=r.replace(i,`
867
- const withNextIntl = createNextIntlPlugin();
868
- export default withNextIntl($1);`):(r+=`
869
- const withNextIntl = createNextIntlPlugin();
870
- `,r+=`export default withNextIntl(nextConfig);
871
- `),await h(t,r)}}},cr=async e=>{let t=he.join(e,a.TYPES_INDEX);if(l(t)){let r=await g(t);r.includes("./i18n")||(r+=`export * from './i18n';
872
- `,await h(t,r))}},pr=async e=>{let t=he.join(e,a.CONFIG_INDEX);if(l(t)){let r=await g(t);r.includes("./app-locales")||(r+=`export * from './app-locales';
873
- `,await h(t,r))}},lr=async e=>{let t=he.join(e,"src/providers/RootProvider.tsx");if(l(t)){let r=await g(t),i="'use client';",n=r.includes(i);if(n&&(r=r.replace(i,"").trim()),r.includes("next-intl")||(r=`import { type AbstractIntlMessages, NextIntlClientProvider } from 'next-intl';
874
- import type { SupportedLocale } from '@/types/i18n';
875
- `+r),n&&(r=`${i}
876
- ${r}`),!r.includes("locale: SupportedLocale")){let o=` locale: SupportedLocale;
877
- messages: AbstractIntlMessages;
878
- timeZone: string;`,s=/(type\s+RootProviderProps\s*=\s*\{)([\s\S]*?)(\};?)/,c=/export const RootProvider = \(\{([\s\S]*?)\}: \{([\s\S]*?)\}\) => \{/;if(s.test(r))r=r.replace(s,(p,u,d,f)=>{let x=d.replace(/\s+$/,"");return`${u}${x}
879
- ${o}
880
- ${f}`}),r=r.replace(/export const RootProvider = \(\{([\s\S]*?)\}:\s*RootProviderProps\)/,(p,u)=>{let d=u.trim().replace(/,$/,"");return`export const RootProvider = ({ ${d?`${d}, `:""}locale, messages, timeZone }: RootProviderProps)`});else{let p=r.match(c);if(p){let u=p[1].trim().replace(/,$/,""),d=p[2].trim().replace(/;$/,""),f=`
881
- ${u?`${u},
882
- `:""}locale,
883
- messages,
884
- timeZone,`,x=`
885
- ${d?`${d};
886
- `:""}locale: SupportedLocale;
887
- messages: AbstractIntlMessages;
888
- timeZone: string;`;r=r.replace(p[0],`export const RootProvider = ({${f}
889
- }: {${x}
890
- }) => {`)}}}if(!r.includes("<NextIntlClientProvider")){let o=r.match(/return\s*(?:\(\s*)?([\s\S]*?)(?:\s*\))?;/);if(o){let s=o[1],c=`<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
891
- {children}
892
- </NextIntlClientProvider>`;if(s.includes("{children}")){let p=s.replace("{children}",c);r=r.replace(o[0],`return (
893
- ${p}
894
- );`)}else r=r.replace(o[0],`return (
895
- <NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
896
- ${s}
897
- </NextIntlClientProvider>
898
- );`)}}await h(t,r)}},Ho=async e=>{let t=he.join(e,a.ROOT_LAYOUT),r=he.join(e,a.ROOT_PAGE),i=he.join(e,"src/app/[locale]");if(await cl.mkdir(i,{recursive:!0}),l(t)){let n=he.join(i,"layout.tsx"),o=await g(t);if(o.includes("next-intl")||(o=`import { routing } from '@/i18n/routing';
899
- import { hasLocale } from 'next-intl';
900
- import { notFound } from 'next/navigation';
901
- import { getMessages, getTimeZone, setRequestLocale } from 'next-intl/server';
902
- import { getLocaleDirection } from '@/lib/config/app-locales';
903
- ${o}`),!o.includes("generateStaticParams")){let p=o.indexOf("};",o.indexOf("export const metadata"));if(p!==-1){let u=p+2;o=o.slice(0,u)+`
904
-
905
- export function generateStaticParams() {
906
- return routing.locales.map((locale) => ({ locale }));
907
- }`+o.slice(u)}}o=o.replace(/Readonly<\{\s*children:\s*React\.ReactNode;\s*}>/,`Readonly<{
908
- children: React.ReactNode;
909
- params: Promise<{ locale: string }>;
910
- }>`);let s=/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,c=o.match(s);if(c){c[1]||(o=o.replace("export default function","export default async function")),o=o.replace(/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,"export default async function RootLayout({ children, params }:");let u=/export default async function RootLayout[\s\S]*?\)\s*\{/,d=o.match(u);if(d&&d.index!==void 0){let f=d.index+d[0].length-1;o=o.slice(0,f+1)+`
911
- const { locale } = await params;
912
-
913
- if (!hasLocale(routing.locales, locale)) {
914
- notFound();
915
- }
916
-
917
- setRequestLocale(locale);
702
+ `,ys=()=>`${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)}
703
+ `,ws=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)}
704
+ `,xs=()=>`# dependencies
705
+ node_modules
706
+ .pnpm-store
707
+
708
+ # turborepo
709
+ .turbo
710
+
711
+ # build and test output
712
+ .next
713
+ out
714
+ coverage
715
+ test-results
716
+ playwright-report
717
+ .vitest
718
+ *.tsbuildinfo
719
+ next-env.d.ts
720
+
721
+ # env
722
+ .env
723
+ .env.*.local
724
+
725
+ # misc
726
+ .DS_Store
727
+ *.pem
728
+ npm-debug.log*
729
+ .pnpm-debug.log*
730
+ `,bs=(e,t)=>`# ${e.name}
731
+
732
+ ${e.description}
733
+
734
+ A pnpm + Turborepo workspace. Each app under \`apps/\` was generated from [@teispace/nextjs-starter](https://github.com/teispace/nextjs-starter) ${t} with \`@teispace/next-maker\`; shared code goes under \`packages/\`.
918
735
 
919
- const [messages, timeZone] = await Promise.all([getMessages(), getTimeZone()]);
920
- `+o.slice(f+1)}o=o.replace(/<RootProvider>/,"<RootProvider locale={locale} messages={messages} timeZone={timeZone}>"),o=o.replace(/<html lang="en"/,"<html lang={locale} dir={getLocaleDirection(locale)}")}await h(n,o),await C(t)}if(l(r)){let n=he.join(i,"page.tsx"),o=await g(r);o.includes("next-intl")||(o=`import { SupportedLocale } from '@/types/i18n';
921
- import { setRequestLocale } from 'next-intl/server';
922
- ${o}`),o=o.replace(/export default function Home\(\)/,`type Props = {
923
- params: Promise<{ locale: string }>;
924
- };
925
-
926
- export default async function Home(props: Props)`);let s=o.indexOf("export default async function Home(props: Props) {");if(s!==-1){let c=`
927
- const locale = (await props.params).locale as SupportedLocale;
928
- setRequestLocale(locale);
929
- `,p=o.indexOf("{",s);o=o.slice(0,p+1)+c+o.slice(p+1)}await h(n,o),await C(r)}};var mr=async e=>{let t=S("Setting up Internationalization (next-intl)..."),r=ci.join(e,".next-maker-temp-i18n");try{let{isSetup:i,reason:n}=await Mo(e);if(i){t.fail(`i18n is already set up (${n}).`);return}await Go(e),await jo(r,t),t.text="Copying i18n files...",await Lo(e,r),t.text="Configuring project structure...",await ar(e),await cr(e),await pr(e),await lr(e),t.text="Migrating to [locale] structure...",await Ho(e),t.text="Installing dependencies...",await z(e,"next-intl");let o=ci.join(e,a.VITEST_CONFIG);if(l(o)){let c=JSON.parse(await g(ci.join(e,a.PACKAGE_JSON))),p={...c.dependencies,...c.devDependencies},u=!!(p["@reduxjs/toolkit"]&&p["react-redux"]);await ce(e,{redux:u,i18n:!0})}t.text="Formatting code...";let s=await A(e);await K(e,s,"lint:fix"),t.succeed(pl.green("Internationalization setup successfully!"))}catch(i){throw t.fail("Failed to setup Internationalization."),i}finally{await v(r)}};P();import ll from"node:fs/promises";import Pt from"node:path";import Uo from"picocolors";w();var ml="src/providers/RootProvider.tsx",Bo=async(e,t)=>{let r=S("Repairing Internationalization..."),i=Pt.join(e,".next-maker-temp-i18n-repair");try{let n=Pt.join(e,a.I18N_DIR),o=Pt.join(e,a.PROXY),s=F(t,a.I18N_DIR)&&!l(n),c=F(t,a.PROXY)&&!l(o),p=[];(s||c)&&(r.text="Fetching assets from starter repo...",await te(i,async x=>{s&&(await ll.cp(Pt.join(x,a.I18N_DIR),n,{recursive:!0}),p.push(a.I18N_DIR)),c&&(await b(Pt.join(x,a.PROXY),o),p.push(a.PROXY))})),U(t,a.NEXT_CONFIG)&&(r.text="Restoring the next-intl plugin wrap...",await ar(e),p.push("createNextIntlPlugin wrap")),U(t,ml)&&(r.text="Restoring <NextIntlClientProvider>...",await lr(e),p.push("<NextIntlClientProvider> wrap")),p.length>0&&(await cr(e),await pr(e)),r.text="Restoring dependencies...";let u=await B(e,t),d=F(t,a.LOCALE_DIR),f=[p.length>0?`restored ${p.join(", ")}`:null,u.length>0?`reinstalled ${u.join(", ")}`:null].filter(Boolean);r.succeed(Uo.green(f.length>0?`Internationalization repaired (${f.join("; ")}).`:"Internationalization repaired.")),d&&console.log(Uo.yellow(` \u26A0\uFE0F ${a.LOCALE_DIR} is missing. Recreating it means moving your app-router pages, so it is left for you: run \`next-maker setup --i18n\` on a clean tree, or move your pages under ${a.LOCALE_DIR}/ by hand.`))}catch(n){throw r.fail("Failed to repair Internationalization."),n}};var zo={id:"i18n",name:"Internationalization",description:"next-intl with [locale] routing, RootProvider, and proxy.ts",detect:async e=>{let{detectProjectSetup:t}=await Promise.resolve().then(()=>(q(),ct));return(await t(e)).hasI18n},files:[{path:"src/i18n",generated:!0,isDir:!0,containsUserContent:!0,removeHint:"holds your translation JSON files \u2014 copy any you want to keep, then delete by hand"},{path:"src/proxy.ts",generated:!0},{path:"src/app/[locale]",generated:!0,isDir:!0,containsUserContent:!0,removeHint:"holds every page you generated under the locale segment \u2014 move them to src/app/ before deleting"}],packages:[{name:"next-intl",kind:"dependency"}],scripts:[],injections:[{file:"next.config.ts",description:"createNextIntlPlugin wrap in next.config.ts",presence:/next-intl\/plugin/,removePattern:fo},{file:"src/providers/RootProvider.tsx",description:"<NextIntlClientProvider> wrap",presence:/NextIntlClientProvider/,removePattern:Ve("NextIntlClientProvider")}],apply:O(mr,Bo)};P();w();import hl from"node:path";P();import dl from"node:path";import ul from"picocolors";w();var Vo=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/,dr=e=>/reactCompiler\s*:/.test(e),vt=e=>{if(dr(e))return e;if(!Vo.test(e))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");return e.replace(Vo,`$1
930
- reactCompiler: true,`)};var ur=async e=>{let t=S("Enabling React Compiler...");try{let r=dl.join(e,a.NEXT_CONFIG);if(!l(r))throw new Error(`${a.NEXT_CONFIG} not found.`);let i=await g(r);if(dr(i)){t.fail("React Compiler is already configured in next.config.ts.");return}await h(r,vt(i)),t.text="Installing babel-plugin-react-compiler...",await At(e,"babel-plugin-react-compiler"),t.succeed(ul.green("React Compiler enabled."))}catch(r){throw t.fail("Failed to enable React Compiler."),r}};P();import fl from"node:path";import gl from"picocolors";w();var Jo=async(e,t)=>{let r=S("Repairing React Compiler...");try{if(U(t,a.NEXT_CONFIG)){let n=fl.join(e,a.NEXT_CONFIG);if(l(n)){let o=await g(n),s=vt(o);s!==o&&await h(n,s)}}let i=await B(e,t);r.succeed(gl.green(i.length>0?`React Compiler repaired (reinstalled ${i.join(", ")}).`:"React Compiler repaired."))}catch(i){throw r.fail("Failed to repair React Compiler."),i}};var Wo={id:"react-compiler",name:"React Compiler",description:"reactCompiler: true in next.config.ts + babel-plugin-react-compiler",detect:async e=>{let t=hl.join(e,a.NEXT_CONFIG);return l(t)?/reactCompiler\s*:\s*true/.test(await g(t)):!1},files:[],packages:[{name:"babel-plugin-react-compiler",kind:"devDependency"}],scripts:[],injections:[{file:a.NEXT_CONFIG,description:"`reactCompiler: true` flag",presence:/reactCompiler\s*:\s*true/,removePattern:/\n\s*reactCompiler:\s*true,?/}],apply:O(ur,Jo)};P();import mi from"node:path";import yl from"picocolors";w();P();import qo from"node:fs/promises";import _e from"node:path";w();var Xo=async(e,t)=>{t.text="Fetching assets from starter repo...",await J(e)},Yo=async(e,t)=>{let r=_e.join(t,"src/providers/StoreProvider.tsx"),i=_e.join(e,a.STORE_PROVIDER);await b(r,i);let n=_e.join(t,"src/store"),o=_e.join(e,a.STORE);await qo.cp(n,o,{recursive:!0})},Ko=async(e,t)=>{let r=_e.join(t,"src/features/counter"),i=_e.join(e,a.COUNTER_FEATURE);await qo.cp(r,i,{recursive:!0});let n=_e.join(i,"components/Counter.tsx");if(await l(n)){let o=await g(n);o=o.replace(/import \{ useTranslations \} from 'next-intl';\n?/,""),o=o.replace(/const t = useTranslations\('Count'\);\n?/,""),o=o.replace(/\{t\('currentCount', \{ count: value \}\)\}/g,"Current Count: {value}"),o=o.replace(/\{t\('increment'\)\}/g,"Increment"),o=o.replace(/\{t\('decrement'\)\}/g,"Decrement"),o=o.replace(/\{t\('reset'\)\}/g,"Reset"),await h(n,o)}};P();w();import pi from"node:path";var Zo=async e=>{let t=pi.join(e,a.STORE),r=pi.join(e,"package.json");if(l(t))return{isSetup:!0,reason:"src/store directory exists"};if(l(r)){let i=JSON.parse(await g(r));if(i.dependencies?.["@reduxjs/toolkit"]||i.devDependencies?.["@reduxjs/toolkit"])return{isSetup:!0,reason:"@reduxjs/toolkit is installed"}}return{isSetup:!1,reason:""}},Qo=async e=>{let t=pi.join(e,a.PROVIDERS_INDEX);if(!await Yt(e)||!l(t))throw new Error("Project structure mismatch. Ensure src/app/layout.tsx (or src/app/[locale]/layout.tsx) and src/providers/index.ts exist.")};P();w();import li from"node:path";var es=async e=>{},fr=async e=>{let t=li.join(e,"src/providers/RootProvider.tsx");if(!l(t))return;let r=await g(t),i="'use client';",n=r.includes(i);if(n&&(r=r.replace(i,"").trim()),r.includes("from '@/store'")||(r=`import type { AppState } from '@/store';
931
- ${r}`),r.includes("from './StoreProvider'")||(r=`${r.includes("from '@/store'"),""}import { StoreProvider } from './StoreProvider';
932
- ${r}`),n&&(r=`${i}
933
- ${r}`),r.includes("preloadedState")||(r=r.replace(/children:\s*React\.ReactNode;/,`children: React.ReactNode;
934
- preloadedState?: Partial<AppState>;`),r=r.replace(/export const RootProvider = \(\{([\s\S]*?)\}:/,(o,s)=>`export const RootProvider = ({${s}, preloadedState }:`),r=r.replace(/,\s*,/g,",").replace(/\{\s*,/g,"{ ").replace(/,\s*\}/g," }")),!r.includes("<StoreProvider")){let o=r.match(/return\s*\(\s*([\s\S]*?)\s*\);/);if(o){let s=o[1];r=r.replace(o[0],`return (
935
- <StoreProvider preloadedState={preloadedState}>
936
- ${s}
937
- </StoreProvider>
938
- );`)}}await h(t,r)},ts=async e=>{let t=[li.join(e,a.ROOT_PAGE),li.join(e,"src/app/[locale]/page.tsx")],r="";for(let n of t)if(l(n)){r=n;break}if(!r)return;let i=await g(r);if(i.includes("Counter")||(i=`import { Counter } from '@/features/counter';
939
- ${i}`),!i.includes("<Counter />")){let n=i.lastIndexOf("</div>"),o=i.lastIndexOf("</main>"),s=o!==-1?o:n;s!==-1&&(i=i.slice(0,s)+`
940
- <div className="mt-8">
941
- <h2 className="mb-4 text-2xl font-bold">Redux Counter</h2>
942
- <Counter />
943
- </div>
944
- `+i.slice(s),await h(r,i))}};var gr=async e=>{let t=S("Setting up Redux Toolkit..."),r=mi.join(e,".next-maker-temp-redux");try{let{isSetup:i,reason:n}=await Zo(e);if(i){t.fail(`Redux is already set up (${n}).`);return}await Qo(e),await Xo(r,t),t.text="Copying Redux files...",await Yo(e,r),t.text="Creating Counter feature...",await Ko(e,r),t.text="Updating providers and pages...",await es(e),await fr(e),await ts(e),t.text="Installing dependencies...",await z(e,"@reduxjs/toolkit"),await z(e,"react-redux"),await z(e,"redux-persist"),await z(e,"react-secure-storage");let o=mi.join(e,a.VITEST_CONFIG);if(l(o)){let c=JSON.parse(await g(mi.join(e,a.PACKAGE_JSON))),p={...c.dependencies,...c.devDependencies};await ce(e,{redux:!0,i18n:!!p["next-intl"]})}t.text="Formatting code...";let s=await A(e);await K(e,s,"lint:fix"),t.succeed(yl.green("Redux Toolkit setup successfully!"))}catch(i){throw t.fail("Failed to setup Redux Toolkit."),i}finally{await v(r)}};P();import xl from"node:fs/promises";import St from"node:path";import wl from"picocolors";w();var Pl="src/providers/RootProvider.tsx",rs=async(e,t)=>{let r=S("Repairing Redux Toolkit..."),i=St.join(e,".next-maker-temp-redux-repair");try{let n=St.join(e,a.STORE),o=St.join(e,a.STORE_PROVIDER),s=F(t,a.STORE)&&!l(n),c=F(t,a.STORE_PROVIDER)&&!l(o),p=[];(s||c)&&(r.text="Fetching assets from starter repo...",await te(i,async f=>{s&&(await xl.cp(St.join(f,a.STORE),n,{recursive:!0}),p.push(a.STORE)),c&&(await b(St.join(f,a.STORE_PROVIDER),o),p.push(a.STORE_PROVIDER))})),U(t,Pl)&&(r.text="Restoring <StoreProvider> wrap...",await fr(e),p.push("<StoreProvider> wrap")),r.text="Restoring dependencies...";let u=await B(e,t),d=[p.length>0?`restored ${p.join(", ")}`:null,u.length>0?`reinstalled ${u.join(", ")}`:null].filter(Boolean);r.succeed(wl.green(d.length>0?`Redux Toolkit repaired (${d.join("; ")}).`:"Redux Toolkit repaired."))}catch(n){throw r.fail("Failed to repair Redux Toolkit."),n}};var is={id:"redux",name:"Redux Toolkit",description:"Redux Toolkit + react-redux + redux-persist + StoreProvider",detect:async e=>{let{detectProjectSetup:t}=await Promise.resolve().then(()=>(q(),ct));return(await t(e)).hasRedux},files:[{path:"src/store",generated:!0,isDir:!0,containsUserContent:!0,removeHint:"holds the rootReducer and any slices you registered \u2014 review before deleting"},{path:"src/providers/StoreProvider.tsx",generated:!0}],packages:[{name:"@reduxjs/toolkit",kind:"dependency"},{name:"react-redux",kind:"dependency"},{name:"redux-persist",kind:"dependency"}],scripts:[],injections:[{file:"src/providers/RootProvider.tsx",description:"<StoreProvider> wrap in RootProvider",presence:/<StoreProvider/,removePattern:Ve("StoreProvider")}],apply:O(gr,rs)};P();w();import bl from"node:path";P();import El from"node:path";import Tl from"picocolors";w();var os=[{key:"X-DNS-Prefetch-Control",value:"on"},{key:"Strict-Transport-Security",value:"max-age=63072000; includeSubDomains; preload"},{key:"X-XSS-Protection",value:"1; mode=block"},{key:"X-Frame-Options",value:"SAMEORIGIN"},{key:"X-Content-Type-Options",value:"nosniff"},{key:"Referrer-Policy",value:"origin-when-cross-origin"}],ns=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/,vl=/^\s+headers\s*:/m,Sl=e=>`
945
- headers: async () => {
946
- return [
947
- {
948
- source: '/:path*',
949
- headers: [
950
- ${e.map(({key:r,value:i})=>` { key: '${r}', value: '${i}' },`).join(`
736
+ \`\`\`
737
+ ${e.name}/
738
+ apps/
739
+ ${e.apps.map(r=>` ${r}/`).join(`
951
740
  `)}
952
- ],
953
- },
954
- ];
955
- },`,Je=e=>vl.test(e),Et=(e,t=os)=>{if(Je(e))return e;if(!ns.test(e))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");return e.replace(ns,`$1${Sl(t)}`)};var hr=async e=>{let t=S("Adding security headers to next.config.ts...");try{let r=El.join(e,a.NEXT_CONFIG);if(!l(r))throw new Error(`${a.NEXT_CONFIG} not found.`);let i=await g(r);if(Je(i)){t.succeed("Security headers already configured.");return}let n=Et(i);await h(r,n),t.succeed(Tl.green("Security headers added."))}catch(r){throw t.fail("Failed to add security headers."),r}};P();import Cl from"node:path";import Rl from"picocolors";w();var ss=async(e,t)=>{if(!U(t,a.NEXT_CONFIG))return;let r=S("Repairing security headers...");try{let i=Cl.join(e,a.NEXT_CONFIG);if(!l(i)){r.fail(`${a.NEXT_CONFIG} not found \u2014 cannot restore security headers.`);return}let n=await g(i),o=Et(n);o!==n&&await h(i,o),r.succeed(Rl.green("Security headers restored."))}catch(i){throw r.fail("Failed to repair security headers."),i}};var as={id:"security-headers",name:"Security Headers",description:"Hardened HTTP headers in next.config.ts",detect:async e=>{let t=bl.join(e,a.NEXT_CONFIG);return l(t)?Je(await g(t)):!1},files:[],packages:[],scripts:[],injections:[{file:a.NEXT_CONFIG,description:"headers() function in next.config.ts",presence:/^\s+headers\s*:/m,removePattern:/\s+headers:\s*async\s*\(\)\s*=>\s*\{[\s\S]*?\n\s{0,2}\},/m}],apply:O(hr,ss)};P();w();import Al from"node:path";P();import Il from"node:fs/promises";import Ce from"node:path";import $l from"picocolors";w();var kl=["vitest","jsdom","@vitejs/plugin-react","@testing-library/dom","@testing-library/jest-dom","@testing-library/react","@testing-library/user-event"],yr=async e=>{let t=S("Setting up testing (Vitest + RTL)..."),r=Ce.join(e,".next-maker-temp-tests");try{let i=Ce.join(e,a.VITEST_CONFIG);if(l(i)){t.fail("Testing is already set up (vitest.config.ts exists).");return}t.text="Fetching assets from starter repo...",await J(r),await b(Ce.join(r,a.VITEST_CONFIG),Ce.join(e,a.VITEST_CONFIG));let n=Ce.join(e,a.TEST_DIR);await Il.mkdir(n,{recursive:!0}),await b(Ce.join(r,a.TEST_DIR,"setup.ts"),Ce.join(n,"setup.ts"));let o=Ce.join(e,a.PACKAGE_JSON),s=JSON.parse(await g(o)),c={...s.dependencies,...s.devDependencies},p=!!(c["@reduxjs/toolkit"]&&c["react-redux"]),u=!!c["next-intl"];await ce(e,{redux:p,i18n:u}),await _(o,f=>(f.scripts=f.scripts??{},f.scripts.test=f.scripts.test??"vitest run",f.scripts["test:watch"]=f.scripts["test:watch"]??"vitest",f.scripts["test:coverage"]=f.scripts["test:coverage"]??"vitest run --coverage",typeof f.scripts.validate=="string"&&!/\byarn\s+test(?![:\w])/.test(f.scripts.validate)&&(f.scripts.validate=f.scripts.validate.replace(/(\byarn\s+check:deprecated\s*)(&&)/,"$1&& yarn test $2")),f)),t.text="Installing testing devDependencies...";let d=await A(e);await fe(e,d,kl),t.text="Formatting code...",await K(e,d,"lint:fix"),t.succeed($l.green("Testing setup complete."))}catch(i){throw t.fail("Failed to set up testing."),i}finally{await v(r)}};P();import Fe from"node:path";import Nl from"picocolors";w();var cs=async(e,t)=>{let r=S("Repairing testing setup..."),i=Fe.join(e,".next-maker-temp-tests-repair");try{let n=F(t,a.VITEST_CONFIG)&&!l(Fe.join(e,a.VITEST_CONFIG)),o=Fe.join(e,a.TEST_SETUP_FILE),s=F(t,a.TEST_DIR)&&!l(o),c=[];if((n||s)&&(r.text="Fetching assets from starter repo...",await te(i,async f=>{n&&(await b(Fe.join(f,a.VITEST_CONFIG),Fe.join(e,a.VITEST_CONFIG)),c.push(a.VITEST_CONFIG)),s&&(await b(Fe.join(f,a.TEST_SETUP_FILE),o),c.push(a.TEST_SETUP_FILE))}),s)){let f=Fe.join(e,a.PACKAGE_JSON),x=l(f)?JSON.parse(await g(f)):{},I={...x.dependencies,...x.devDependencies};await ce(e,{redux:!!(I["@reduxjs/toolkit"]&&I["react-redux"]),i18n:!!I["next-intl"]}),c.push("test/test-utils.tsx")}r.text="Restoring testing dependencies...";let p=await B(e,t),u=await Ee(e,t),d=[c.length>0?`restored ${c.join(", ")}`:null,p.length>0?`reinstalled ${p.join(", ")}`:null,u.length>0?`reset ${u.join(", ")}`:null].filter(Boolean);r.succeed(Nl.green(d.length>0?`Testing setup repaired (${d.join("; ")}).`:"Testing setup repaired."))}catch(n){throw r.fail("Failed to repair testing setup."),n}};var ps={id:"tests",name:"Tests",description:"Vitest + React Testing Library + jsdom",detect:async e=>l(Al.join(e,a.VITEST_CONFIG)),files:[{path:a.VITEST_CONFIG,generated:!0},{path:"test",generated:!0,isDir:!0,containsUserContent:!0,removeHint:"holds setup.ts / test-utils.tsx and any helpers you added \u2014 review before deleting"}],packages:[{name:"vitest",kind:"devDependency"},{name:"jsdom",kind:"devDependency"},{name:"@vitejs/plugin-react",kind:"devDependency"},{name:"@testing-library/dom",kind:"devDependency"},{name:"@testing-library/jest-dom",kind:"devDependency"},{name:"@testing-library/react",kind:"devDependency"},{name:"@testing-library/user-event",kind:"devDependency"}],scripts:[{name:"test",expectedValue:"vitest run"},{name:"test:watch",expectedValue:"vitest"}],injections:[],apply:O(yr,cs)};w();import jl from"node:path";P();import ds from"node:path";import Ol from"picocolors";w();var _l={npm:"npm run",yarn:"yarn",pnpm:"pnpm",bun:"bun run"},Fl=e=>{let t=_l[e];return["ci:check","type-check","check:deprecated","test","build"].map(r=>`${t} ${r}`).join(" && ")},Tt=(e,t)=>{let r={...e.scripts??{}},i={...e.devDependencies??{}};return r["env:sync"]||(r["env:sync"]="tsx scripts/sync-env.ts"),r["check:deprecated"]||(r["check:deprecated"]="tsx scripts/check-deprecated.ts"),r["type-check"]||(r["type-check"]="tsc --noEmit"),r.validate||(r.validate=Fl(t)),!i.tsx&&!Object.hasOwn(e.dependencies??{},"tsx")&&(i.tsx="*"),{...e,scripts:r,devDependencies:i}};var ls=`import * as fs from 'node:fs';
956
- import * as path from 'node:path';
957
-
958
- const checkOnly = process.argv.includes('--check');
959
-
960
- const envPath = path.join(process.cwd(), '.env');
961
- const examplePath = path.join(process.cwd(), '.env.example');
962
-
963
- if (!fs.existsSync(envPath)) {
964
- console.log('\u26A0\uFE0F No .env file found. Skipping sync.');
965
- process.exit(0);
966
- }
967
-
968
- const envContent = fs.readFileSync(envPath, 'utf-8');
969
- const lines = envContent.split('\\n');
970
-
971
- let isPublicBlock = false;
972
-
973
- const exampleLines = lines.map((line) => {
974
- const trimmed = line.trim();
975
-
976
- // Preserve empty lines and reset public block state
977
- if (!trimmed) {
978
- isPublicBlock = false;
979
- return line;
980
- }
981
-
982
- // Preserve comments
983
- if (trimmed.startsWith('#')) {
984
- if (line.toLowerCase().includes('-public')) {
985
- isPublicBlock = true;
986
- }
987
- return line;
988
- }
989
-
990
- // Parse KEY=VALUE
991
- const match = line.match(/^([^=]+)=(.*)$/);
992
-
993
- if (match) {
994
- const key = match[1];
741
+ packages/ shared libraries (see below)
742
+ turbo.json task graph and caching
743
+ pnpm-workspace.yaml workspace members and install policy
744
+ \`\`\`
995
745
 
996
- // Public marker: explicit "# -public" comment, or "KEY=VAL # -public"
997
- if (isPublicBlock || line.toLowerCase().includes('-public')) {
998
- isPublicBlock = false;
999
- return line;
1000
- }
746
+ ## Getting started
1001
747
 
1002
- isPublicBlock = false;
1003
- return \`\${key}=\`;
1004
- }
748
+ \`\`\`bash
749
+ pnpm install
750
+ pnpm dev # every app
751
+ pnpm --filter ${e.apps[0]} dev # one app
752
+ \`\`\`
1005
753
 
1006
- isPublicBlock = false;
1007
- return line;
1008
- });
754
+ Each app reads its own \`.env\` (copy \`apps/<app>/.env.example\`). Ports: set \`PORT\` per app (\`PORT=3001 pnpm --filter ${e.apps[1]??e.apps[0]} dev\`).
1009
755
 
1010
- const newExampleContent = exampleLines.join('\\n');
756
+ ## Scripts
1011
757
 
1012
- let currentExampleContent = '';
1013
- if (fs.existsSync(examplePath)) {
1014
- currentExampleContent = fs.readFileSync(examplePath, 'utf-8');
1015
- }
758
+ | Script | Purpose |
759
+ | :-- | :-- |
760
+ | \`pnpm build\` | build every app (Turborepo caches unchanged apps) |
761
+ | \`pnpm lint\` / \`pnpm ci:check\` | Biome per app and at the root |
762
+ | \`pnpm type-check\`, \`pnpm check:deprecated\`, \`pnpm test\`, \`pnpm test:e2e\` | per-app gates through Turborepo |
763
+ | \`pnpm validate\` | everything CI runs |
1016
764
 
1017
- if (currentExampleContent === newExampleContent) {
1018
- console.log('\u2728 .env.example is already up to date');
1019
- process.exit(0);
1020
- }
765
+ ## Adding a shared package
1021
766
 
1022
- if (checkOnly) {
1023
- console.error('\u274C .env.example is out of sync with .env. Run "yarn env:sync" to update.');
1024
- process.exit(1);
1025
- }
767
+ \`\`\`bash
768
+ mkdir -p packages/ui && cd packages/ui
769
+ pnpm init # name it @${e.name}/ui, "type": "module"
770
+ cd ../../apps/${e.apps[0]} && pnpm add @${e.name}/ui --workspace
771
+ \`\`\`
1026
772
 
1027
- fs.writeFileSync(examplePath, newExampleContent);
1028
- console.log('\u2705 Synchronized .env.example with .env');
1029
- `,ms=`/**
1030
- * Walks every identifier in the project and flags any whose resolved TypeScript
1031
- * symbol carries a \`@deprecated\` JSDoc tag. Catches deprecated API usage that
1032
- * \`tsc --noEmit\` does not surface (deprecations are "suggestion"-level, not
1033
- * errors) and that Biome does not yet detect.
1034
- *
1035
- * Exits 1 if any deprecated usage is found (so it can gate CI).
1036
- */
1037
- import path from 'node:path';
1038
- import ts from 'typescript';
773
+ Give the package a \`build\` script (or point \`exports\` at TypeScript sources and add it to \`transpilePackages\` in the app's \`next.config.ts\`). Turborepo runs \`^build\` before an app builds.
1039
774
 
1040
- const cwd = process.cwd();
1041
- const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, 'tsconfig.json');
775
+ ## Adding an app
1042
776
 
1043
- if (!configPath) {
1044
- console.error('tsconfig.json not found');
1045
- process.exit(1);
1046
- }
777
+ \`\`\`bash
778
+ cd apps && npx @teispace/next-maker init <name> --yes --set hooks=false --set commitizen=false --set ci=false --set docker=false --no-git
779
+ \`\`\`
1047
780
 
1048
- const raw = ts.readConfigFile(configPath, ts.sys.readFile).config;
1049
- const parsed = ts.parseJsonConfigFileContent(raw, ts.sys, path.dirname(configPath));
781
+ Git hooks, CI, and Docker live at the workspace root; apps keep their own tests, lint, and type-check.
1050
782
 
1051
- const program = ts.createProgram({
1052
- rootNames: parsed.fileNames,
1053
- options: parsed.options,
1054
- });
783
+ ## Docker
1055
784
 
1056
- const checker = program.getTypeChecker();
785
+ \`apps/<app>/Dockerfile\` (generated with \`--docker\`; add later by copying one) builds from the workspace root with \`turbo prune\`, so an image contains only that app and its workspace dependencies:
1057
786
 
1058
- function isDeprecated(sym: ts.Symbol | undefined): boolean {
1059
- if (!sym) return false;
1060
- return sym.getJsDocTags(checker).some((tag) => tag.name === 'deprecated');
1061
- }
787
+ \`\`\`bash
788
+ docker build -f apps/${e.apps[0]}/Dockerfile -t ${e.apps[0]} .
789
+ docker compose up --build
790
+ \`\`\`
1062
791
 
1063
- let hits = 0;
1064
-
1065
- for (const sourceFile of program.getSourceFiles()) {
1066
- if (sourceFile.fileName.includes('node_modules')) continue;
1067
- if (!sourceFile.fileName.startsWith(cwd)) continue;
1068
-
1069
- const visit = (node: ts.Node): void => {
1070
- if (ts.isIdentifier(node)) {
1071
- const sym = checker.getSymbolAtLocation(node);
1072
- if (isDeprecated(sym)) {
1073
- const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
1074
- console.log(
1075
- \`\${path.relative(cwd, sourceFile.fileName)}:\${line + 1}:\${character + 1} \${node.text} (@deprecated)\`,
1076
- );
1077
- hits++;
1078
- }
1079
- }
1080
- ts.forEachChild(node, visit);
1081
- };
792
+ ## Dependencies
793
+
794
+ Ranges shared by every app live in the \`catalog:\` section of \`pnpm-workspace.yaml\`; apps reference them as \`"next": "catalog:"\`. Bump a shared version there once. Add an app-specific dependency the normal way (\`pnpm --filter ${e.apps[0]} add <pkg>\`).
795
+
796
+ ## Maintenance
797
+
798
+ \`cd apps/<app> && npx @teispace/next-maker doctor --compile\` checks an app against the starter; \`setup --set <option>=<value>\` changes its features.
799
+ `,vs=()=>`name: CI
800
+
801
+ on:
802
+ push:
803
+ branches: [main]
804
+ pull_request:
805
+ branches: [main]
806
+
807
+ concurrency:
808
+ group: \${{ github.workflow }}-\${{ github.ref }}
809
+ cancel-in-progress: \${{ github.ref != 'refs/heads/main' }}
810
+
811
+ env:
812
+ NEXT_PUBLIC_APP_URL: https://ci.example.com
813
+ NEXT_TELEMETRY_DISABLED: 1
814
+ TURBO_TELEMETRY_DISABLED: 1
815
+
816
+ jobs:
817
+ validate:
818
+ name: Lint, types, tests, build
819
+ runs-on: ubuntu-latest
820
+ timeout-minutes: 20
821
+ steps:
822
+ - uses: actions/checkout@v6
823
+ - uses: pnpm/action-setup@v4
824
+ - uses: actions/setup-node@v6
825
+ with:
826
+ node-version-file: .nvmrc
827
+ cache: pnpm
828
+ - run: pnpm install --frozen-lockfile
829
+ - name: Restore Turborepo cache
830
+ uses: actions/cache@v4
831
+ with:
832
+ path: .turbo
833
+ key: turbo-\${{ runner.os }}-\${{ github.sha }}
834
+ restore-keys: |
835
+ turbo-\${{ runner.os }}-
836
+ - run: pnpm ci:check
837
+ - run: pnpm type-check
838
+ - run: pnpm check:deprecated
839
+ - run: pnpm test
840
+ - run: pnpm build
841
+ `,$s=()=>`const config = {
842
+ '*': ['biome check --write --no-errors-on-unmatched --files-ignore-unknown=true'],
843
+ };
1082
844
 
1083
- visit(sourceFile);
1084
- }
845
+ export default config;
846
+ `,Ps=()=>`const config = {
847
+ extends: ['@commitlint/config-conventional'],
848
+ rules: {
849
+ 'header-max-length': [2, 'always', 150],
850
+ 'scope-enum': [0],
851
+ },
852
+ };
1085
853
 
1086
- if (hits === 0) {
1087
- console.log('\u2713 No deprecated API usage found.');
1088
- process.exit(0);
1089
- }
854
+ export default config;
855
+ `,ks=()=>({"pre-commit":`#!/usr/bin/env sh
856
+ pnpm lint-staged
857
+ `,"commit-msg":`#!/usr/bin/env sh
858
+ pnpm exec commitlint --edit "$1"
859
+ `,"pre-push":`#!/usr/bin/env sh
860
+ pnpm ci:check && pnpm type-check && pnpm check:deprecated && pnpm test
861
+ `}),Ss=(e,t="24")=>`# syntax=docker.io/docker/dockerfile:1
862
+ # Build from the workspace root:
863
+ # docker build -f apps/${e}/Dockerfile -t ${e} .
864
+
865
+ FROM node:${t}-alpine AS base
866
+ ENV PNPM_HOME=/pnpm
867
+ ENV PATH=$PNPM_HOME:$PATH
868
+ RUN corepack enable && apk add --no-cache libc6-compat
869
+
870
+ # Prune the workspace down to this app and its dependencies.
871
+ FROM base AS pruner
872
+ WORKDIR /app
873
+ RUN pnpm add -g turbo@2
874
+ COPY . .
875
+ RUN turbo prune ${e} --docker
876
+
877
+ # Install with only the pruned lockfile so dependency layers cache well.
878
+ FROM base AS installer
879
+ WORKDIR /app
880
+ COPY --from=pruner /app/out/json/ ./
881
+ COPY --from=pruner /app/pnpm-workspace.yaml ./
882
+ COPY --from=pruner /app/.npmrc ./
883
+ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
884
+
885
+ # Build with the full pruned source.
886
+ FROM base AS builder
887
+ WORKDIR /app
888
+ ARG NEXT_PUBLIC_API_URL
889
+ ARG NEXT_PUBLIC_APP_URL
890
+ ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
891
+ ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
892
+ ENV NEXT_TELEMETRY_DISABLED=1
893
+ ENV TURBO_TELEMETRY_DISABLED=1
894
+ ENV BUILD_STANDALONE=true
895
+ COPY --from=installer /app/ ./
896
+ COPY --from=pruner /app/out/full/ ./
897
+ RUN pnpm turbo run build --filter=${e}
898
+
899
+ FROM base AS runner
900
+ WORKDIR /app
901
+ ENV NODE_ENV=production
902
+ ENV NEXT_TELEMETRY_DISABLED=1
903
+ ENV PORT=3000
904
+ ENV HOSTNAME=0.0.0.0
905
+ RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
906
+ COPY --from=builder --chown=nextjs:nodejs /app/apps/${e}/public ./apps/${e}/public
907
+ COPY --from=builder --chown=nextjs:nodejs /app/apps/${e}/.next/standalone ./
908
+ COPY --from=builder --chown=nextjs:nodejs /app/apps/${e}/.next/static ./apps/${e}/.next/static
909
+ USER nextjs
910
+ EXPOSE 3000
911
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
912
+ CMD wget -qO- http://127.0.0.1:3000/robots.txt >/dev/null || exit 1
913
+ CMD ["node", "apps/${e}/server.js"]
914
+ `,Es=e=>`services:
915
+ ${e.map((r,n)=>` ${r}:
916
+ build:
917
+ context: .
918
+ dockerfile: apps/${r}/Dockerfile
919
+ args:
920
+ NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL}
921
+ NEXT_PUBLIC_APP_URL: \${${r.toUpperCase().replace(/-/g,"_")}_URL:-http://localhost:${3e3+n}}
922
+ restart: unless-stopped
923
+ ports:
924
+ - "${3e3+n}:3000"
925
+ environment:
926
+ NODE_ENV: production
927
+ NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL}
928
+ API_INTERNAL_URL: \${API_INTERNAL_URL}`).join(`
1090
929
 
1091
- console.log(\`\\n\${hits} deprecated symbol use(s) found.\`);
1092
- process.exit(1);
1093
- `,Ct=[{relative:"scripts/sync-env.ts",content:ls},{relative:"scripts/check-deprecated.ts",content:ms}];var xr=async e=>{let t=S("Adding validation scripts...");try{let r=!1;for(let{relative:o,content:s}of Ct){let c=ds.join(e,o);l(c)||(await h(c,s),r=!0)}let i=await A(e),n=!1;await _(ds.join(e,a.PACKAGE_JSON),o=>{let s=o.devDependencies?.tsx,c=Tt(o,i);return n=!s&&!!c.devDependencies?.tsx,c}),n&&(t.text="Installing tsx...",await fe(e,i,["tsx"])),t.succeed(Ol.green(r?"Validation scripts added (env:sync, check:deprecated, validate).":"Validation scripts already present \u2014 refreshed package.json."))}catch(r){throw t.fail("Failed to add validation scripts."),r}};P();import us from"node:path";import Dl from"picocolors";w();var fs=async(e,t)=>{let r=S("Repairing validation scripts...");try{let i=[];for(let{relative:p,content:u}of Ct){if(!F(t,p))continue;let d=us.join(e,p);l(d)||(await h(d,u),i.push(p))}let n=await B(e,t),o=us.join(e,a.PACKAGE_JSON);if(l(o)){let p=await A(e);await _(o,u=>Tt(u,p))}let s=await Ee(e,t),c=[i.length>0?`rewrote ${i.join(", ")}`:null,n.length>0?`reinstalled ${n.join(", ")}`:null,s.length>0?`reset ${s.join(", ")}`:null].filter(Boolean);r.succeed(Dl.green(c.length>0?`Validation scripts repaired (${c.join("; ")}).`:"Validation scripts repaired."))}catch(i){throw r.fail("Failed to repair validation scripts."),i}};var gs=["scripts/sync-env.ts","scripts/check-deprecated.ts"],hs={id:"validate-scripts",name:"Validation Scripts",description:"sync-env, check-deprecated, and the validate chain",detect:async e=>gs.every(t=>l(jl.join(e,t))),files:gs.map(e=>({path:e,generated:!0})),packages:[{name:"tsx",kind:"devDependency"}],scripts:[{name:"env:sync",expectedValue:"tsx scripts/sync-env.ts"},{name:"check:deprecated",expectedValue:"tsx scripts/check-deprecated.ts"},{name:"type-check",expectedValue:"tsc --noEmit"},{name:"validate"}],injections:[],apply:O(xr,fs)};import{access as Ul,readFile as Bl}from"node:fs/promises";import Es from"node:path";P();import fi from"node:path";import Ll from"picocolors";w();P();import di from"node:fs/promises";import Rt from"node:path";w();var ys=async(e,t)=>{t.text="Downloading assets from starter repository...",await J(e)},xs=async(e,t,r)=>{let i=Rt.join(e,a.WS_UTILS);await di.cp(Rt.join(t,a.WS_UTILS),i,{recursive:!0,force:!0});let n=Rt.join(e,a.WS_SLICE_FILE);await b(Rt.join(t,a.WS_SLICE_FILE),n),r.keepTests||await ui(i)},ui=async e=>{let t=async r=>{let i=await di.readdir(r,{withFileTypes:!0});for(let n of i){let o=Rt.join(r,n.name);if(n.isDirectory()){if(n.name==="__test-utils__"){await v(o);continue}await t(o)}else n.isFile()&&/\.test\.tsx?$/.test(n.name)&&await di.unlink(o)}};await t(e)};P();w();import ws from"node:path";var Ps=async e=>{let t=ws.join(e,a.WS_UTILS);return l(t)?{isSetup:!0,reason:"src/lib/utils/ws/ already exists"}:{isSetup:!1}},vs=async e=>{let t=[],r=[{rel:"src/lib",label:"src/lib"},{rel:"src/store",label:"src/store (Redux Toolkit not installed?)"},{rel:a.ROOT_REDUCER,label:"src/store/rootReducer.ts (Redux not initialised?)"},{rel:a.STORE_PROVIDER,label:"src/providers/StoreProvider.tsx (Redux not initialised?)"}];for(let{rel:i,label:n}of r)l(ws.join(e,i))||t.push(`- ${n}`);if(t.length>0)throw new Error(`WebSocket setup requires Redux Toolkit. Missing:
1094
- ${t.join(`
1095
930
  `)}
1096
-
1097
- Run \`next-maker setup --redux\` first.`)};var wr=async e=>{let t=S("Setting up WebSocket layer..."),r=fi.join(e,".next-maker-temp-ws");try{let{isSetup:i,reason:n}=await Ps(e);if(i){t.fail(`WebSocket layer is already set up (${n}).`);return}await vs(e),await ys(r,t),t.text="Copying WebSocket files...";let o=l(fi.join(e,a.VITEST_CONFIG));await xs(e,r,{keepTests:o}),t.text="Registering wsReducer...",await Ft(e),t.text="Mounting WS bridge in StoreProvider...",await Dt(e),t.text="Ensuring test/setup.ts mocks...",await jt(e),t.text="Installing socket.io-client...";let s=fi.join(e,a.PACKAGE_JSON),c=JSON.parse(await g(s));({...c.dependencies,...c.devDependencies})["socket.io-client"]||await z(e,"socket.io-client@^4.8.3"),t.text="Formatting code...";let u=await A(e);await K(e,u,"lint:fix"),t.succeed(Ll.green("WebSocket layer setup successfully!"))}catch(i){throw t.fail("Failed to setup WebSocket layer."),i}finally{await v(r)}};P();import Ml from"node:fs/promises";import We from"node:path";import Gl from"picocolors";w();var Hl="socket.io-client@^4.8.3",Ss=async(e,t)=>{let r=S("Repairing WebSocket layer..."),i=We.join(e,".next-maker-temp-ws-repair");try{let n=We.join(e,a.WS_UTILS),o=We.join(e,a.WS_SLICE_FILE),s=F(t,a.WS_UTILS)&&!l(n),c=F(t,a.WS_SLICE_FILE)&&!l(o),p=[];if(s||c){r.text="Fetching assets from starter repo...";let f=l(We.join(e,a.VITEST_CONFIG));await te(i,async x=>{s&&(await Ml.cp(We.join(x,a.WS_UTILS),n,{recursive:!0,force:!0}),f||await ui(n),p.push(a.WS_UTILS)),c&&(await b(We.join(x,a.WS_SLICE_FILE),o),p.push(a.WS_SLICE_FILE))})}U(t,a.ROOT_REDUCER)&&(r.text="Re-registering wsReducer...",await Ft(e),p.push("wsReducer registration")),U(t,a.STORE_PROVIDER)&&(r.text="Re-mounting the WS bridge...",await Dt(e),p.push("attachWsBridge mount")),await jt(e);let u=[];qt(t,"dependency").includes("socket.io-client")&&(r.text="Installing socket.io-client...",await z(e,Hl),u.push("socket.io-client"));let d=[p.length>0?`restored ${p.join(", ")}`:null,u.length>0?`reinstalled ${u.join(", ")}`:null].filter(Boolean);r.succeed(Gl.green(d.length>0?`WebSocket layer repaired (${d.join("; ")}).`:"WebSocket layer repaired."))}catch(n){throw r.fail("Failed to repair WebSocket layer."),n}};var zl=async e=>{try{let t=JSON.parse(await Bl(Es.join(e,"package.json"),"utf-8"));return{...t.dependencies,...t.devDependencies}["socket.io-client"]?(await Ul(Es.join(e,"src/lib/utils/ws")),!0):!1}catch{return!1}},Ts={id:"ws",name:"WebSocket Client",description:"socket.io-client + lazy WS singleton + Redux slice + hooks",detect:zl,files:[{path:"src/lib/utils/ws",generated:!0,isDir:!0,containsUserContent:!0,removeHint:"holds the ws client + any feature event maps you added \u2014 review before deleting"},{path:"src/store/slices/ws.slice.ts",generated:!0}],packages:[{name:"socket.io-client",kind:"dependency"}],scripts:[],injections:[{file:"src/store/rootReducer.ts",description:"wsReducer registration in rootReducer",presence:/ws:\s*wsReducer\b/,removePattern:Lt},{file:"src/providers/StoreProvider.tsx",description:"attachWsBridge mount in StoreProvider",presence:/attachWsBridge\(wsClient,\s*store\.dispatch\)/,removePattern:Ot}],apply:O(wr,Ss)};P();w();import Oe from"node:path";var Cs=async e=>{let t=Oe.join(e,a.PACKAGE_JSON);return l(t)?JSON.parse(await g(t)):null},Vl=async(e,t)=>{let r=Oe.join(e,a.PACKAGE_JSON);await h(r,`${JSON.stringify(t,null,2)}
1098
- `)},Rs=(e,t)=>(t==="dependency"?e.dependencies:e.devDependencies)??{},bs=e=>[e.file,...e.alternativeFiles??[]],qe=async(e,t)=>{let r=await e.detect(t),i=[];if(!r)return{manifest:e,installed:!1,drift:[]};for(let o of e.files)o.required!==!1&&(l(Oe.join(t,o.path))||i.push({kind:"missingFile",file:o.path}));let n=await Cs(t);if(n){for(let s of e.packages)Rs(n,s.kind)[s.name]||i.push({kind:"missingPackage",name:s.name,depKind:s.kind});let o=n.scripts??{};for(let s of e.scripts){let c=o[s.name];c?s.expectedValue&&c!==s.expectedValue&&i.push({kind:"mismatchedScript",name:s.name,expected:s.expectedValue,actual:c}):i.push({kind:"missingScript",name:s.name,expected:s.expectedValue})}}for(let o of e.injections){let c=bs(o).filter(u=>l(Oe.join(t,u)));if(c.length===0){i.push({kind:"missingInjection",file:o.file,description:o.description});continue}let p=!1;for(let u of c){let d=await g(Oe.join(t,u));if(o.presence.test(d)){p=!0;break}}p||i.push({kind:"missingInjection",file:c[0],description:o.description})}return{manifest:e,installed:r,drift:i}},Pr=async(e,t,r={})=>{let i=r.dryRun??!1,n={filesRemoved:[],packagesUninstalled:[],scriptsRemoved:[],blocksStripped:[],manualCleanup:[]};for(let c of e.injections)for(let p of bs(c)){let u=Oe.join(t,p);if(!l(u))continue;if(!c.removePattern){n.manualCleanup.push({file:p,description:c.description});continue}let d=await g(u);if(!c.presence.test(d))continue;let f;if(typeof c.removePattern=="function"){let x=c.removePattern(d);if(x===null){n.manualCleanup.push({file:p,description:c.description});continue}f=x}else f=d.replace(c.removePattern,"");f!==d?(i||await h(u,f),n.blocksStripped.push({file:p,description:c.description})):n.manualCleanup.push({file:p,description:c.description})}let{rm:o}=await import("node:fs/promises");for(let c of e.files){if(!c.generated)continue;let p=Oe.join(t,c.path);if(l(p)){if(c.containsUserContent){let u=c.removeHint?`${c.path} \u2014 ${c.removeHint}`:`${c.path} (may contain user-authored files; review before deleting)`;n.manualCleanup.push({file:c.path,description:u});continue}i||await o(p,{recursive:!0,force:!0}),n.filesRemoved.push(c.path)}}let s=await Cs(t);if(s){let c=!1;if(s.scripts)for(let p of e.scripts)s.scripts[p.name]&&(i||delete s.scripts[p.name],n.scriptsRemoved.push(p.name),c=!0);for(let p of e.packages)Rs(s,p.kind)[p.name]&&n.packagesUninstalled.push(p.name);c&&!i&&await Vl(t,s)}if(!i&&n.packagesUninstalled.length>0){let c=await A(t);await $r(t,c,n.packagesUninstalled)}return n};var De=[as,hs,wo,Wo,ho,ps,$o,Do,is,Ts,zo],Is=e=>De.find(t=>t.id===e);var $s=e=>{switch(e.kind){case"missingFile":return`missing file: ${e.file}`;case"missingPackage":return`missing ${e.depKind==="dependency"?"dep":"devDep"}: ${e.name}`;case"missingScript":return`missing script: ${e.name}`;case"mismatchedScript":return`script "${e.name}" expected ${$.dim(e.expected)} but got ${$.dim(e.actual)}`;case"missingInjection":return`missing block in ${e.file}: ${e.description}`}},Jl=e=>{let t=e.filter(i=>i.installed),r=t.filter(i=>i.drift.length>0);return{installedCount:t.length,driftedCount:r.length,cleanCount:t.length-r.length,driftedResults:r}},Wl={fixed:$.green("\u2713"),stillDrifted:$.red("\u2717"),noApply:$.yellow("\u26A0\uFE0F")},ql={fixed:$.green("FIXED"),stillDrifted:$.red("STILL DRIFTED"),noApply:$.yellow("NO AUTOMATIC FIX AVAILABLE")},Xl=async(e,t)=>{let r=[];for(let i of e){let{manifest:n,drift:o}=i;if(!n.apply){r.push({id:n.id,name:n.name,status:"noApply",remaining:o});continue}let s;try{await n.apply(t,o)}catch(p){s=p instanceof Error?p.message:String(p)}let c=await qe(n,t);r.push({id:n.id,name:n.name,status:c.drift.length===0&&!s?"fixed":"stillDrifted",remaining:c.drift,error:s})}return r},ks=e=>{e.command("doctor").description("Diagnose drift between the project and known feature manifests").option("--fix","Re-apply manifests that report drift (idempotent)").option("--feature <id>","Only check this manifest id (e.g. redux, security-headers)").option("--json","Print machine-readable JSON instead of the human report").action(async t=>{try{let r=process.cwd(),i=t.feature?De.filter(u=>u.id===t.feature):De;if(t.feature&&i.length===0)throw new Error(`Unknown feature "${t.feature}". Valid: ${De.map(u=>u.id).join(", ")}.`);let n=[];for(let u of i)n.push(await qe(u,r));t.json&&(m(JSON.stringify(n.map(u=>({id:u.manifest.id,name:u.manifest.name,installed:u.installed,drift:u.drift})),null,2)),process.exit(n.some(u=>u.drift.length>0)?1:0)),m($.cyan(`
1099
- \u{1FA7A} Project Doctor
1100
- `));for(let u of n){let{manifest:d,installed:f,drift:x}=u;if(!f){m(` ${$.dim("\u2014")} ${$.dim(d.name)} ${$.dim("(not installed)")}`);continue}if(x.length===0){m(` ${$.green("\u2713")} ${d.name}`);continue}m(` ${$.yellow("!")} ${$.bold(d.name)} ${$.dim(`(${x.length} issue${x.length===1?"":"s"})`)}`);for(let I of x)m(` ${$.yellow("\u2022")} ${$s(I)}`)}let o=Jl(n);m(""),m($.dim(`${o.cleanCount} clean, ${o.driftedCount} drifted, ${n.length-o.installedCount} not installed`)),o.driftedCount===0&&(m($.green(`
1101
- \u2713 No drift detected.
1102
- `)),process.exit(0)),t.fix||(m($.dim(`
1103
- Run with --fix to re-apply drifted features.
1104
- `)),process.exit(1)),m($.cyan(`
1105
- \u{1F527} Applying fixes...
1106
- `));let s=await Xl(o.driftedResults,r);m("");for(let u of s){m(` ${Wl[u.status]} ${u.name} ${ql[u.status]}`);for(let d of u.remaining)m(` ${$.yellow("\u2022")} ${$s(d)}`);u.error&&m(` ${$.red("\u2022")} ${u.error}`)}let c=s.filter(u=>u.status==="fixed").length,p=s.length-c;m(""),m($.dim(`${c} fixed, ${p} still drifted`)),p>0&&(m($.yellow(`
1107
- ! Doctor could not fix everything.
1108
- `)),process.exit(1)),m($.green(`
1109
- \u2713 All drift fixed.
1110
- `)),process.exit(0)}catch(r){E(`${r instanceof Error?r.message:r}`),process.exit(1)}})};import Xe from"picocolors";P();w();import gi from"node:path";var Yl=/^[A-Z][A-Z0-9_]*$/,_s=e=>{if(!Yl.test(e))throw new Error(`Invalid env var name "${e}". Use UPPER_SNAKE_CASE (e.g. SENTRY_DSN, NEXT_PUBLIC_API_URL).`)},ye=(e,t)=>t?e.startsWith("NEXT_PUBLIC_")?e:`NEXT_PUBLIC_${e}`:e,hi=e=>e.public?"client":"server",Kl=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,"\\'")}')`}},Zl=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(i=>`'${i.replace(/'/g,"\\'")}'`).join(", ")}])`}default:return"e.string()"}},Fs=e=>{let t=ye(e.name,e.public),r=Zl(e),i=e.default!==void 0,n=!e.required&&!i;if(i&&(r+=Kl(e)),n&&(r+=".optional()"),e.description){let o=e.description.replace(/'/g,"\\'");r+=`
1111
- .describe('${o}')`}return` ${t}: ${r},`},Os=e=>{let t=ye(e.name,e.public);return` ${t}: process.env.${t},`},Ql=e=>new RegExp(`(${e}:\\s*\\{)([\\s\\S]*?)(\\n\\s*\\})`),Ns=/(runtimeEnv:\s*\{)([\s\S]*?)(\n\s*\})/,As=(e,t,r,i)=>{let n=t.replace(/\s+$/,""),o=n.length===0||/,\s*$/.test(n)?n:`${n},`;return`${e}${o}
1112
- ${i}
1113
- ${r.replace(/^\n/,"")}`},Ds=(e,t)=>{let r=ye(t.name,t.public),i=hi(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 o=Ql(i),s=e.match(o);if(!s)throw new Error(`Could not locate the \`${i}: { \u2026 }\` group in src/lib/env/index.ts.`);let[,c,p,u]=s,d=Fs(t),f=e.replace(o,As(c,p,u,d)),x=f.match(Ns);if(!x)throw new Error("Could not locate the `runtimeEnv: { \u2026 }` map in src/lib/env/index.ts.");let[,I,H,k]=x;return f=f.replace(Ns,As(I,H,k,Os(t))),f},em="# -public",tm=e=>{let t=ye(e.name,e.public),r=e.default??"",i=e.public?` ${em}`:"";return`${t}=${r}${i}`},js=(e,t)=>{let r=ye(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let n=e===""||e.endsWith(`
1114
- `)?"":`
1115
- `,o=e===""?"":`
1116
- `,s=t.description?`# ${t.description}
1117
- `:"";return`${e}${n}${o}${s}${tm(t)}
1118
- `},Ls=(e,t)=>{let r=ye(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let n=e===""||e.endsWith(`
1119
- `)?"":`
1120
- `,o=t.default??"";return`${e}${n}${r}=${o}
1121
- `},yi=async(e,t)=>{_s(t.name);let r=gi.join(e,"src/lib/env/index.ts");if(!l(r))throw new Error("src/lib/env/index.ts not found \u2014 run `next-maker init` first.");let i=await g(r),n=Ds(i,t),o=n!==i;o&&await h(r,n);let s=gi.join(e,a.ENV_EXAMPLE),c=!1;if(l(s)){let d=await g(s),f=js(d,t);c=f!==d,c&&await h(s,f)}let p=gi.join(e,".env"),u=!1;if(l(p)){let d=await g(p),f=Ls(d,t);u=f!==d,u&&await h(p,f)}return{schemaUpdated:o,envExampleUpdated:c,envUpdated:u,group:hi(t)}};var Ms=new Set(["string","url","number","boolean","enum"]),rm=e=>{let t=(e??"string").toLowerCase();if(!Ms.has(t))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(Ms).join(", ")}.`);return t},im=e=>{if(!e)return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length===0?void 0:t},Gs=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 i=process.cwd();if(m(Xe.cyan(`
1122
- \u{1F510} Env Var Generator
1123
- `)),r.required&&r.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let n=rm(r.type),o=im(r.enum);if(n==="enum"&&(!o||o.length===0))throw new Error("--type=enum requires at least one value via --enum a,b,c.");let s={name:t,type:n,required:!!r.required,default:r.default,description:r.describe,enumValues:o,public:!!r.public};y.start("Updating src/lib/env/index.ts, .env.example, and .env...");let c=await yi(i,s);y.succeed("Env var written");let p=ye(s.name,s.public);m(Xe.green(`
1124
- \u2728 ${p} declared in the ${c.group} group.
1125
- `)),m(Xe.dim("Touched:")),m(Xe.dim(` ${c.schemaUpdated?"\u270F\uFE0F ":"\u23ED "} src/lib/env/index.ts`)),m(Xe.dim(` ${c.envExampleUpdated?"\u270F\uFE0F ":"\u23ED "} .env.example`)),m(Xe.dim(` ${c.envUpdated?"\u270F\uFE0F ":"\u23ED "} .env`)),m("")}catch(i){y.fail("Env var declaration failed"),E(`${i instanceof Error?i.message:i}`),process.exit(1)}})};import Er from"node:path";import Le from"picocolors";import nm from"enquirer";var{prompt:om}=nm,Hs=async(e={})=>e.source?{source:e.source}:{source:(await om({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 sm,readFile as am,stat as cm}from"node:fs/promises";import je from"node:path";var Us=async e=>{try{return await sm(e),!0}catch{return!1}},pm=async e=>{try{return(await cm(e)).isDirectory()}catch{return!1}},vr=async e=>{let t=[je.join(e,"src","app"),je.join(e,"app")];for(let r of t)if(await pm(r))return r;return null},lm=["next-pwa","@ducanh2912/next-pwa","@serwist/next","serwist"],xi=async e=>{let t={isPwa:!1},r=[je.join(e,"public","manifest.json"),je.join(e,"public","manifest.webmanifest"),je.join(e,"public","site.webmanifest")];for(let n of r)if(await Us(n)){t.publicManifestPath=n,t.isPwa=!0;break}let i=await vr(e);if(i)for(let n of["ts","tsx","js"]){let o=je.join(i,`manifest.${n}`);if(await Us(o)){t.appManifestPath=o,t.isPwa=!0;break}}try{let n=JSON.parse(await am(je.join(e,"package.json"),"utf-8")),o={...n.dependencies,...n.devDependencies};for(let s of lm)if(o[s]){t.framework=s,t.isPwa=!0;break}}catch{}return t};import{mkdir as gm,readFile as Zs,stat as hm,writeFile as ym}from"node:fs/promises";import me from"node:path";import xm from"sharp";import dm from"png-to-ico";import bt from"sharp";var Bs=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),wi=(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 i=t/2;return`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}"><circle cx="${i}" cy="${i}" r="${i}" fill="white"/></svg>`}case"rounded":{let i=Math.round(t*r/100);return`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}"><rect width="${t}" height="${t}" rx="${i}" ry="${i}" fill="white"/></svg>`}case"squircle":return mm(t)}},mm=e=>{let t=e,r=t*.225,i=[`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="${Bs(i)}" fill="white"/></svg>`},zs=(e,t)=>`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${e}"><rect width="${e}" height="${e}" fill="${Bs(t)}"/></svg>`;var Ye=async e=>{let{size:t,paddingPercent:r,bg:i,fit:n,shape:o,radiusPercent:s,quality:c,forceSquare:p,sourceBuffer:u}=e,d=Math.max(1,Math.round(t*(1-r*2/100))),f=n==="cover"?"cover":"contain",x=await bt(u,{density:384}).resize(d,d,{fit:f,background:{r:0,g:0,b:0,alpha:0}}).png().toBuffer(),I=Math.round((t-d)/2),H=i==="transparent"?{r:0,g:0,b:0,alpha:0}:await Pi(i),k=await bt({create:{width:t,height:t,channels:4,background:H}}).composite([{input:x,left:I,top:I}]).png().toBuffer();if(!p&&(o!=="square"||n==="clip")){let kt=o==="square"?"rounded":o,$a=Buffer.from(wi(kt,t,s));k=await bt(k).composite([{input:$a,blend:"dest-in"}]).png({compressionLevel:Vs(c)}).toBuffer()}else k=await bt(k).png({compressionLevel:Vs(c)}).toBuffer();return k},Pi=async e=>{let t=await bt(Buffer.from(zs(1,e))).raw().toBuffer({resolveWithObject:!0}),[r,i,n,o=255]=t.data;return{r,g:i,b:n,alpha:o/255}},Vs=e=>{let t=Math.round((100-e)/100*9);return Math.max(0,Math.min(9,t))},Js=async e=>{if(e.length===0)throw new Error("No frames provided to encodeIco");return dm(e)};import{readFile as um,writeFile as zT}from"node:fs/promises";var Ws=e=>e.map(({src:t,size:r})=>({src:t,sizes:`${r}x${r}`,type:"image/png",purpose:"any maskable"})),qs=async(e,t)=>{let r=await um(e,"utf-8"),i=JSON.parse(r),n=Array.isArray(i.icons)?i.icons:[],o=new Map;for(let s of[...n,...t])s&&typeof s.src=="string"&&o.set(s.src,s);return i.icons=[...o.values()],`${JSON.stringify(i,null,2)}
1126
- `};var Xs=e=>`icons: [
1127
- ${e.map(r=>` { src: '${r.src}', sizes: '${r.sizes}', type: '${r.type}'${r.purpose?`, purpose: '${r.purpose}'`:""} }`).join(`,
1128
- `)},
1129
- ],`,Ys=(e,t,r)=>`${JSON.stringify({name:e,short_name:e,start_url:"/",display:"standalone",background_color:"#ffffff",theme_color:t,icons:r},null,2)}
1130
- `;import{stat as fm}from"node:fs/promises";import vi from"node:path";var Ks=[".png",".jpg",".jpeg",".webp",".svg",".avif"],Sr=async e=>{if(!e)throw new Error("Source image path is required (use --path).");let t=vi.isAbsolute(e)?e:vi.resolve(process.cwd(),e),r;try{r=await fm(t)}catch{throw new Error(`Source image not found: ${t}`)}if(!r.isFile())throw new Error(`Source is not a file: ${t}`);let i=vi.extname(t).toLowerCase();if(!Ks.includes(i))throw new Error(`Unsupported source format '${i}'. Use one of: ${Ks.join(", ")}`);return{absolutePath:t,ext:i,bytes:r.size,isVector:i===".svg"}};var Si=(e,t)=>{if(!e)return t;let r=e.split(",").map(i=>Number.parseInt(i.trim(),10)).filter(i=>Number.isFinite(i)&&i>0&&i<=1024);if(r.length===0)throw new Error(`Invalid --sizes value: '${e}'. Provide comma-separated positive integers.`);return r};var wm=async e=>{try{return await hm(e),!0}catch{return!1}},Pm=async(e,t,r)=>{let i=[];for(let n of e){if(await wm(n.outPath)&&!t&&!n.alwaysWrite){i.push({path:n.outPath,bytes:n.bytes.length,skipped:!0});continue}r||(await gm(me.dirname(n.outPath),{recursive:!0}),await ym(n.outPath,n.bytes)),i.push({path:n.outPath,bytes:n.bytes.length})}return i},Ei=async(e,t)=>{let r=await Sr(t.source),i=await Zs(r.absolutePath),n=t.ogSource?await Zs((await Sr(t.ogSource)).absolutePath):i,o=[],s=[];for(let d of t.icoSizes)s.push(await Ye({sourceBuffer:i,size:d,shape:"square",radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality,forceSquare:!0}));let c=await Js(s);if(o.push({outPath:me.join(t.outDir,"favicon.ico"),bytes:c}),t.emitIcon){let d=await Ye({sourceBuffer:i,size:512,shape:t.shape,radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality});o.push({outPath:me.join(t.outDir,"icon.png"),bytes:d})}if(t.emitApple){let d=t.bg==="transparent"?"#ffffff":t.bg,f=await Ye({sourceBuffer:i,size:180,shape:t.shape,radiusPercent:t.radius,bg:d,paddingPercent:t.padding,fit:t.fit,quality:t.quality});o.push({outPath:me.join(t.outDir,"apple-icon.png"),bytes:f})}if(t.emitOg){let d=await Qs(n,t,!1);o.push({outPath:me.join(t.outDir,"opengraph-image.png"),bytes:d})}if(t.emitTwitter){let d=await Qs(n,t,!0);o.push({outPath:me.join(t.outDir,"twitter-image.png"),bytes:d})}let p;if(t.pwa||t.pwaInit){let d=await xi(e);if(t.pwa&&!d.isPwa)throw new Error("No PWA setup detected. Use --pwa-init to bootstrap a manifest, or set up next-pwa/serwist first.");let f=me.join(e,"public"),x=[192,512],I=[];for(let k of x){let Z=await Ye({sourceBuffer:i,size:k,shape:t.shape,radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality}),kt=`icon-${k}.png`;o.push({outPath:me.join(f,kt),bytes:Z}),I.push({src:`/${kt}`,size:k})}let H=Ws(I);if(d.publicManifestPath){let k=await qs(d.publicManifestPath,H);o.push({outPath:d.publicManifestPath,bytes:Buffer.from(k,"utf-8"),alwaysWrite:!0})}else if(d.appManifestPath)p=Xs(H);else if(t.pwaInit){let k=me.join(f,"manifest.webmanifest"),Z=Ys(me.basename(e)||"App","#000000",H);o.push({outPath:k,bytes:Buffer.from(Z,"utf-8")})}}return{outputs:await Pm(o,t.force,t.dryRun),pwaSnippet:p}},Qs=async(e,t,r)=>{let n=r?600:630,o=Math.round(Math.min(1200,n)*(1-t.padding*2/100)),s=await Ye({sourceBuffer:e,size:o,shape:t.shape,radiusPercent:t.radius,bg:"transparent",paddingPercent:0,fit:t.fit,quality:t.quality}),c=t.bg==="transparent"?"#ffffff":t.bg,p=await Pi(c),u=Math.round((1200-o)/2),d=Math.round((n-o)/2);return xm({create:{width:1200,height:n,channels:4,background:p}}).composite([{input:s,left:u,top:d}]).png().toBuffer()};var ea=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 vm=["square","rounded","circle","squircle"],Sm=["contain","cover","clip"],ta=(e,t,r,i)=>{if(!e)return r;if(!t.includes(e))throw new Error(`Invalid value for --${i}: '${e}'. Allowed: ${t.join(", ")}`);return e},Ti=(e,t,r,i,n)=>{if(e===void 0)return t;let o=Number(e);if(!Number.isFinite(o)||o<i||o>n)throw new Error(`Invalid value for --${r}: '${e}'. Must be a number between ${i}\u2013${n}.`);return o},ia=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(Le.cyan(`
1131
- \u{1F3A8} Favicon Generator
1132
- `));let i=await Hs({source:t.path}),n=await Em(r,t.out),o=ta(t.shape,vm,"square","shape"),s=ta(t.fit,Sm,"contain","fit"),c=Ti(t.radius,20,"radius",0,50),p=Ti(t.padding,0,"padding",0,30),u=Ti(t.quality,90,"quality",1,100),d=Si(t.sizes,[16,32,48]),f=t.bg??"transparent";Rm(f);let x=t.all===!0,I={source:i.source,outDir:n,force:!!t.force,dryRun:!!t.dryRun,emitIcon:!!t.icon||x,emitApple:!!t.apple||x,emitOg:!!t.og||x,emitTwitter:!!t.twitter,ogSource:t.ogSource,pwa:!!t.pwa,pwaInit:!!t.pwaInit,shape:o,radius:c,bg:f,padding:p,fit:s,icoSizes:d,quality:u};y.start("Generating favicon assets...");let H=await Ei(r,I);y.succeed(I.dryRun?"Favicon plan ready":"Favicon assets generated"),m("");for(let k of H.outputs){let Z=Er.relative(r,k.path);k.skipped?m(Le.yellow(` \u21B7 skipped ${Z} (exists; use --force to overwrite)`)):I.dryRun?m(Le.dim(` \u{1F4C4} would write ${Z} (${ra(k.bytes)})`)):m(Le.green(` \u2713 ${Z} `)+Le.dim(`(${ra(k.bytes)})`))}H.pwaSnippet&&(m(""),m(Le.cyan("Add this to your typed manifest (src/app/manifest.ts):")),m(Le.dim(H.pwaSnippet))),m("")}catch(r){y.fail("Favicon generation failed");let i=r instanceof Error?r.message:String(r);E(i),process.exit(1)}})},Em=async(e,t)=>{if(t)return Er.isAbsolute(t)?t:Er.resolve(e,t);let r=await vr(e);return r||(Mi("Could not detect app/ or src/app/ \u2014 falling back to src/app"),Er.join(e,"src","app"))},Tm=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,Cm=/^(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/]+\)$/i,Rm=e=>{if(e!=="transparent"&&!Tm.test(e)&&!Cm.test(e)&&!ea.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().`)},ra=e=>e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`;import ca from"node:path";import V from"picocolors";q();q();import Gm from"node:path";import{readFile as bm,writeFile as Im}from"node:fs/promises";import $m from"node:path";var It=async e=>{let{serviceName:t,projectPath:r}=e,i=G(t),n=$m.join(r,"src","lib","config","app-apis.ts");try{let o=await bm(n,"utf-8");if(o.includes(`${i}:`))return;if(!o.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let s=` ${i}: {
1133
- base: '/${t}',
1134
- getAll: '/${t}',
1135
- },`;o=ot(o,s),await Im(n,o)}catch(o){throw new Error(`Failed to register API endpoints: ${o}`,{cause:o})}};import{readFile as km,writeFile as Nm}from"node:fs/promises";import Am from"node:path";w();var Ci=async(e,t,r)=>{let i=Am.join(e,"src","lib","config","app-paths.ts");if(!l(i))return;let n=await km(i,"utf-8"),o=G(t);if(n.includes(`${o}:`))return;let s=n.lastIndexOf("} as const;");if(s===-1)return;let c=` ${o}: '${r}',
1136
- `;n=n.slice(0,s)+c+n.slice(s),await Nm(i,n)};P();w();import Tr from"node:path";var _m=e=>new RegExp(`<\\s*${e}\\b`),na=(e,t)=>{if(_m(t).test(e))return e;let r=/^(?<indent>[ \t]*)\{children\}\s*$/m,i=e.match(r);if(!i||i.index===void 0)throw new Error("Could not locate `{children}` line in RootProvider \u2014 expected a line containing only `{children}`.");let n=i.groups?.indent??"",o=`${n} `,s=`${n}<${t}>
1137
- ${o}{children}
1138
- ${n}</${t}>`;return e.replace(r,s)},oa=(e,t)=>{let r=`export * from './${t}';`;if(e.includes(r))return e;let i=e.split(`
1139
- `),n=[],o=[];for(let f of i)/^export \* from '\.\/.*';\s*$/.test(f)?n.push(f):o.push(f);let s=[...n,r].sort(),c=i.findIndex(f=>/^export \* from '\.\//.test(f));if(c===-1)return`${r}
1140
- ${e.replace(/^\n+/,"")}`;let p=i.slice(0,c),u=i.findIndex((f,x)=>x>c&&!/^export \* from '\.\//.test(f)),d=u===-1?[]:i.slice(u);return[...p,...s,...d].join(`
1141
- `)},Fm=a.PROVIDERS,Om=a.ROOT_PROVIDER,Dm=a.PROVIDERS_INDEX,sa=async(e,t={fileExists:l,readFile:g})=>{let r=Tr.join(e,Om);if(t.fileExists(r))return r;let i=Tr.join(e,Fm);if(!t.fileExists(i))return null;let{readdir:n}=await import("node:fs/promises"),s=(await n(i).catch(()=>[])).filter(p=>p.endsWith(".tsx")),c=null;for(let p of s){let u=Tr.join(i,p),d=await t.readFile(u);if(!/\{children\}/.test(d))continue;let x=((d.split("{children}")[0]??"").match(/<[A-Z][A-Za-z0-9_]*\b/g)??[]).length;(!c||x>c.depth)&&(c={file:u,depth:x})}return c?.file??null},Ri=async e=>{let{projectPath:t,componentName:r,fileBaseName:i}=e,n=await sa(t);if(n){let c=await g(n),p=c;(!/from '\.\//.test(c)||!c.includes(`{ ${r} }`))&&(p=nt(p,`import { ${r} } from './${i}';`)),p=na(p,r),p!==c&&await h(n,p)}let o=Tr.join(t,Dm),s=null;if(l(o)){let c=await g(o),p=oa(c,i);p!==c&&await h(o,p),s=o}return{rootProviderFile:n,barrelFile:s}};w();import{readFile as jm,writeFile as Lm}from"node:fs/promises";import Mm from"node:path";var bi=async(e,t)=>{let r=Mm.join(e,"src","i18n","translations","en.json");if(!l(r))return;let i=await jm(r,"utf-8"),n=JSON.parse(i);n[t]||(n[t]={title:t,description:`${t} page description`},await Lm(r,`${JSON.stringify(n,null,2)}
1142
- `))};var Ii=()=>[{name:"Project setup detected",spinnerText:"Detecting project setup...",execute:async e=>{e.detection=await M(e.projectPath)}},{name:"Feature files generated",spinnerText:"Generating feature files...",execute:async e=>{await Wr({name:e.featureName,outputPath:e.featurePath,createStore:e.createStore,persistStore:e.persistStore,createService:e.createService,httpClient:e.httpClient})}},{name:"API endpoints registered",spinnerText:"Registering API endpoints...",shouldSkip:e=>!e.createService,execute:async e=>{await It({serviceName:e.featureName,projectPath:e.projectPath})}},{name:"Feature registered in rootReducer",spinnerText:"Registering feature in rootReducer...",shouldSkip:e=>!e.createStore||!e.detection.hasRedux,execute:async e=>{await ke({projectPath:e.projectPath,name:e.featureName,persist:e.persistStore,importPath:Gm.join(e.basePath,e.featureName,"store")})}}];var Me=async(e,t)=>{for(let r of e)if(!r.shouldSkip?.(t)){t.spinner.start(r.spinnerText);try{await r.execute(t),t.spinner.succeed(r.name)}catch(i){throw t.spinner.fail(r.name),i}}};var $i=()=>[{name:"Service files generated",spinnerText:"Generating service files...",execute:async e=>{await pt({name:e.serviceName,outputPath:e.servicePath,httpClient:e.httpClient})}},{name:"API endpoints registered",spinnerText:"Registering API endpoints...",execute:async e=>{await It({serviceName:e.serviceName,projectPath:e.projectPath})}}];import Hm from"node:path";var ki=()=>[{name:"Slice files generated",spinnerText:"Generating slice files...",execute:async e=>{await dt({name:e.sliceName,outputPath:e.slicePath,persist:e.persistSlice})}},{name:"Slice registered in rootReducer",spinnerText:"Registering slice in rootReducer...",execute:async e=>{await ke({projectPath:e.projectPath,name:e.sliceName,persist:e.persistSlice,importPath:Hm.join(e.basePath,e.sliceName)})}}];import Um from"enquirer";var{prompt:Bm}=Um,aa=async(e,t,r,i,n,o,s)=>{let c=[];e||c.push({type:"input",name:"featureName",message:"What is the feature name?",initial:"my-feature",validate:u=>/^[a-z0-9-]+$/.test(u)?!0:"Feature name must be lowercase and contain only alphanumeric characters and hyphens."}),t&&i===void 0&&n===void 0&&(c.push({type:"confirm",name:"createStore",message:"Generate Redux store for this feature?",initial:!0}),c.push({type:"confirm",name:"persistStore",message:"Enable persistence for this store?",initial:!1,skip(){return!this.state.answers.createStore}})),r&&r!=="none"&&o===void 0&&s===void 0&&(c.push({type:"confirm",name:"createService",message:"Generate API service for this feature?",initial:!0}),r==="both"&&c.push({type:"select",name:"selectedHttpClient",message:"Which HTTP client to use for the service?",choices:["fetch","axios"],initial:0,skip(){return!this.state.answers.createService}}));let p=c.length>0?await Bm(c):{};return{featureName:e||p.featureName,hasRedux:t||!1,createStore:i===!0?!1:n!==void 0?!0:p.createStore||!1,persistStore:n==="persist"?!0:n==="no-persist"?!1:p.persistStore||!1,httpClient:r||"none",createService:o===!0?!1:s!==void 0?!0:p.createService||!1,selectedHttpClient:s||p.selectedHttpClient||(r==="both"?"fetch":r!=="none"?r:void 0)}};var pa=e=>{e.command("feature [name]").description("Generate a new feature module").option("--skip-store","Skip Redux store generation").option("--store <type>","Generate Redux store with persistence option (persist|no-persist)").option("--skip-service","Skip API service generation").option("--service <client>","Generate API service with specific HTTP client (axios|fetch)").option("--path <path>","Custom path for feature generation (default: src/features)").action(async(t,r)=>{try{let i=process.cwd();zm(r),m(V.cyan(`
1143
- \u{1F3AF} Feature Generator
1144
- `));let n=Ii();y.start("Detecting project setup...");let{detectProjectSetup:o}=await Promise.resolve().then(()=>(q(),ct)),s=await o(i);y.succeed("Project setup detected"),m(V.dim(` Redux: ${s.hasRedux?"\u2713":"\u2717"}`)),m(V.dim(` HTTP Client: ${s.httpClient}`)),m(V.dim(` i18n: ${s.hasI18n?"\u2713":"\u2717"}
1145
- `));let c=await aa(t,s.hasRedux,s.httpClient,r.skipStore,r.store,r.skipService,r.service);W(c.featureName,"feature name"),r.path&&pe(r.path,"path");let p=r.path||ca.join("src","features"),u=Gt(i,p,c.featureName);await Se(i,c.featureName,p)&&(E(`Feature '${c.featureName}' already exists at ${p}!`),process.exit(1)),await Me(n.slice(1),{projectPath:i,spinner:y,featureName:c.featureName,basePath:p,featurePath:u,createStore:c.createStore,persistStore:c.persistStore,createService:c.createService,httpClient:c.selectedHttpClient,detection:s}),Vm(c,p)}catch(i){y.fail("Feature generation failed"),E(`${i}`),process.exit(1)}})},zm=e=>{e.store&&!["persist","no-persist"].includes(e.store)&&(E("Invalid --store option. Use: persist or no-persist"),process.exit(1)),e.service&&!["axios","fetch"].includes(e.service)&&(E("Invalid --service option. Use: axios or fetch"),process.exit(1)),e.skipStore&&e.store&&(E("Cannot use --skip-store and --store together"),process.exit(1)),e.skipService&&e.service&&(E("Cannot use --skip-service and --service together"),process.exit(1))},Vm=(e,t)=>{let r=ca.join(t,e.featureName);m(V.green(`
1146
- \u2728 Feature '${e.featureName}' created successfully!
1147
- `)),m(V.dim("Generated files:")),m(V.dim(` \u{1F4C2} ${r}/`)),m(V.dim(" \u251C\u2500\u2500 components/")),m(V.dim(" \u251C\u2500\u2500 hooks/")),m(V.dim(" \u251C\u2500\u2500 types/")),e.createStore&&m(V.dim(" \u251C\u2500\u2500 store/")),e.createService&&m(V.dim(" \u251C\u2500\u2500 services/")),m(V.dim(` \u2514\u2500\u2500 index.ts
1148
- `)),m(V.cyan("Next steps:"));let i=t.replace(/^src\//,"@/");m(V.dim(` 1. Import and use the feature: import { ${e.featureName} } from '${i}/${e.featureName}'`)),e.createStore&&m(V.dim(` 2. Customize your Redux slice in: ${r}/store/`)),e.createService&&m(V.dim(` 3. Add API methods in: ${r}/services/${e.featureName}.service.ts`)),m("")};import Ni from"node:path";import Ke from"picocolors";q();import Jm from"enquirer";var{prompt:Wm}=Jm,la=async e=>e?{hookName:e}:await Wm({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 ma=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 i=process.cwd();m(Ke.cyan(`
1149
- \u{1FA9D} Hook Generator
1150
- `));let n=await M(i),o=await la(t);W(o.hookName,"hook name"),r.feature&&pe(r.feature,"feature path");let c=`use${N(o.hookName)}`,p=qm(r,n.hasTests);y.start("Generating hook..."),await Xr({name:o.hookName,projectPath:i,isClient:r.client??!0,featurePath:r.feature}),y.succeed("Hook generated");let u=r.feature?Ni.join(i,r.feature,"hooks",`${c}.ts`):Ni.join(i,"src/hooks",`${c}.ts`),d=null;p&&(y.start("Generating test..."),d=await ge({projectPath:i,sourceFile:u,kind:"hook",symbolName:c,hookUsesStore:n.hasRedux&&!!r.feature}),y.succeed("Test generated"));let f=r.feature?`${r.feature}/hooks/${c}.ts`:`src/hooks/${c}.ts`;m(Ke.green(`
1151
- \u2728 Hook '${c}' created successfully!
1152
- `)),m(Ke.dim(` \u{1F4C4} ${f}`)),d&&m(Ke.dim(` \u{1F9EA} ${Ni.relative(i,d)}`)),m(""),m(Ke.cyan("Usage:"));let x=r.feature?`${r.feature.replace(/^src\//,"@/")}/hooks/${c}`:`@/hooks/${c}`;m(Ke.dim(` import { ${c} } from '${x}';`)),m("")}catch(i){y.fail("Hook generation failed"),E(`${i}`),process.exit(1)}})},qm=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import Cr from"picocolors";q();var da=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 i=process.cwd();m(Cr.cyan(`
1153
- \u{1F4D0} Layout Generator
1154
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected");let o=n.hasI18n&&r.locale!==!1;m(Cr.dim(` i18n: ${o?"\u2713":"\u2717"}
1155
- `)),y.start("Generating layout...");let s=await Yr({segment:t,at:r.at,projectPath:i,hasI18n:o,withGroup:!!r.group});y.succeed("Layout generated"),m(Cr.green(`
1156
- \u2728 ${s.componentName} created!
1157
- `)),m(Cr.dim(` \u{1F4C4} ${s.displayPath}`)),m("")}catch(i){y.fail("Layout generation failed"),E(`${i instanceof Error?i.message:i}`),process.exit(1)}})};import de from"picocolors";w();q();import Xm from"enquirer";var{prompt:Ym}=Xm,ua=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"code",message:"Locale code (e.g., es, fr, de):",validate:n=>n?/^[a-z]{2}(-[A-Z]{2})?$/.test(n)?!0:"Use format: xx or xx-XX (e.g., es, pt-BR)":"Locale code is required"}),r.push({type:"input",name:"name",message:"Language name (e.g., Spanish, French):",validate:n=>n?!0:"Language name is required"},{type:"input",name:"country",message:"Country (e.g., Spain, France):",validate:n=>n?!0:"Country is required"},{type:"input",name:"flag",message:"Flag emoji (e.g., \u{1F1EA}\u{1F1F8}, \u{1F1EB}\u{1F1F7}):",validate:n=>n?!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 i=await Ym(r);return{code:e??i.code,name:i.name,country:i.country,flag:i.flag,copyTranslations:t.copyTranslations??i.copyTranslations??!1}};var fa=e=>{e.command("locale [code]").description("Add a new locale/language").option("--copy-translations","Copy translations from English instead of empty values").action(async(t,r)=>{try{let i=process.cwd();m(de.cyan(`
1158
- \u{1F30D} Locale Generator
1159
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected"),n.hasI18n||(y.fail("i18n is not set up in this project"),E("Please set up internationalization first"),m(de.dim(`
1160
- Run: npx @teispace/next-maker setup --i18n
1161
- `)),process.exit(1));let o=await ua(t,{copyTranslations:r.copyTranslations});W(o.code,"locale code");let s=Gt(i,"src","i18n","translations",`${o.code}.json`);l(s)&&(E(`Locale '${o.code}' already exists!`),process.exit(1)),y.start("Adding locale..."),await Kr({...o,projectPath:i}),y.succeed("Locale added"),m(de.green(`
1162
- \u2728 Locale '${o.code}' (${o.name}) added successfully!
1163
- `)),m(de.dim("Updated files:")),m(de.dim(` \u{1F4C4} src/i18n/translations/${o.code}.json`)),m(de.dim(" \u{1F4DD} src/types/i18n.ts (SupportedLocale type)")),m(de.dim(" \u{1F4DD} src/lib/config/app-locales.ts")),m(""),m(de.cyan("Next steps:")),m(de.dim(` 1. Translate: src/i18n/translations/${o.code}.json`)),m(de.dim(` 2. Visit: http://localhost:3000/${o.code}`)),m("")}catch(i){y.fail("Locale generation failed"),E(`${i}`),process.exit(1)}})};import ha from"node:path";import ne from"picocolors";q();import Km from"enquirer";var{prompt:Zm}=Km,ga=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"pageName",message:"Page name (kebab-case):",validate:n=>n?/^[a-z][a-z0-9-]*$/.test(n)?!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 i=r.length>0?await Zm(r):{};return{pageName:e??i.pageName,withLoading:t.loading??i.withLoading??!0,withError:t.error??i.withError??!0}};var ya=e=>{e.command("page [name]").description("Generate a new page/route").option("--dynamic <param>","Create dynamic route with parameter (e.g., id)").option("--loading","Generate loading.tsx").option("--error","Generate error.tsx").action(async(t,r)=>{try{let i=process.cwd();m(ne.cyan(`
1164
- \u{1F4C4} Page Generator
1165
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected"),m(ne.dim(` i18n: ${n.hasI18n?"\u2713":"\u2717"}
1166
- `));let o=await ga(t,{loading:r.loading,error:r.error}),s=o.pageName;W(s,"page name"),r.dynamic&&W(r.dynamic,"dynamic param");let c=N(s),p=n.hasI18n?"src/app/[locale]":"src/app";await Se(i,s,p)&&(E(`Page '${s}' already exists at ${p}/${s}!`),process.exit(1)),y.start("Generating page files..."),await Qr({name:s,projectPath:i,hasI18n:n.hasI18n,dynamic:r.dynamic,withLoading:r.loading??o.withLoading,withError:r.error??o.withError}),y.succeed("Page files generated"),y.start("Registering route...");let d=r.dynamic?`/${s}/[${r.dynamic}]`:`/${s}`;await Ci(i,s,d),y.succeed("Route registered"),n.hasI18n&&(y.start("Adding translation namespace..."),await bi(i,c),y.succeed("Translation namespace added"));let f=r.dynamic?ha.join(p,s,`[${r.dynamic}]`):ha.join(p,s);m(ne.green(`
1167
- \u2728 Page '${s}' created successfully!
1168
- `)),m(ne.dim("Generated files:")),m(ne.dim(` \u{1F4C2} ${f}/`)),m(ne.dim(" \u251C\u2500\u2500 page.tsx")),(r.loading??o.withLoading)&&m(ne.dim(" \u251C\u2500\u2500 loading.tsx")),(r.error??o.withError)&&m(ne.dim(" \u2514\u2500\u2500 error.tsx")),m(""),n.hasI18n&&(m(ne.cyan("Next steps:")),m(ne.dim(` 1. Add translations in: src/i18n/translations/en.json \u2192 "${c}"`)),m(ne.dim(` 2. Visit: http://localhost:3000/${s}`)),m(""))}catch(i){y.fail("Page generation failed"),E(`${i}`),process.exit(1)}})};import Ai from"node:path";import Re from"picocolors";var xa=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();m(Re.cyan(`
1169
- \u{1F50C} Provider Generator
1170
- `)),y.start("Generating provider...");let{componentName:i,fileBaseName:n,providerFile:o}=await ti({name:t,projectPath:r});y.succeed("Provider generated"),y.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:s,barrelFile:c}=await Ri({projectPath:r,componentName:i,fileBaseName:n});y.succeed("Provider registered"),m(Re.green(`
1171
- \u2728 ${i} created!
1172
- `)),m(Re.dim("Generated files:")),m(Re.dim(` \u{1F4C4} ${Ai.relative(r,o)}`)),s?m(Re.dim(` \u270F\uFE0F ${Ai.relative(r,s)} \u2014 wrapped {children}`)):(m(Re.yellow(`
1173
- \u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),m(Re.dim(` <${i}>{children}</${i}>`))),c&&m(Re.dim(` \u270F\uFE0F ${Ai.relative(r,c)} \u2014 added re-export`)),m("")}catch(r){y.fail("Provider generation failed"),E(`${r instanceof Error?r.message:r}`),process.exit(1)}})};import Qm from"enquirer";import X from"picocolors";var{prompt:ed}=Qm,td=e=>{let t=[];for(let r of e.filesRemoved)t.push(` ${X.red("-")} delete: ${r}`);for(let r of e.blocksStripped)t.push(` ${X.red("-")} strip block: ${r.description} ${X.dim(`(${r.file})`)}`);for(let r of e.scriptsRemoved)t.push(` ${X.red("-")} remove script: ${r}`);for(let r of e.packagesUninstalled)t.push(` ${X.red("-")} uninstall: ${r}`);for(let r of e.manualCleanup)t.push(` ${X.yellow("?")} manual cleanup: ${r.description} ${X.dim(`(${r.file})`)}`);return t},rd=e=>e.filesRemoved.length===0&&e.blocksStripped.length===0&&e.scriptsRemoved.length===0&&e.packagesUninstalled.length===0&&e.manualCleanup.length===0,wa=e=>{e.command("remove <feature>").alias("uninstall").description("Reverse a feature install (uses the feature manifest)").option("-y, --yes","Skip confirmation prompt").option("--dry-run","Show what would change without writing").action(async(t,r)=>{try{let i=process.cwd(),n=Is(t);if(!n)throw new Error(`Unknown feature "${t}". Valid: ${De.map(p=>p.id).join(", ")}.`);if(m(X.cyan(`
1174
- \u{1F5D1} Remove ${n.name}
1175
- `)),!(await qe(n,i)).installed){m(X.dim(`${n.name} is not installed \u2014 nothing to remove.`));return}y.start("Computing changes...");let s=await Pr(n,i,{dryRun:!0});if(y.succeed("Computed plan"),rd(s)){m(X.yellow(`
1176
- \u26A0\uFE0F Manifest had no removable artifacts. Nothing to do.
1177
- `));return}m(X.bold(`
1178
- Planned changes:
1179
- `));for(let p of td(s))m(p);if(m(""),r.dryRun){m(X.dim(`--dry-run set \u2014 exiting without writing.
1180
- `));return}if(!r.yes){let{confirm:p}=await ed({type:"confirm",name:"confirm",message:`Apply the changes above to remove ${n.name}?`,initial:!1});if(!p){m(X.yellow(`
1181
- Aborted.
1182
- `));return}}y.start(`Removing ${n.name}...`);let c;if(n.remove?(await n.remove(i),c=s):c=await Pr(n,i),y.succeed(`${n.name} removed`),m(X.green(`
1183
- \u2713 ${n.name} removed.`)),c.manualCleanup.length>0){m(X.yellow(`
1184
- \u26A0\uFE0F Manual cleanup still required:`));for(let p of c.manualCleanup)m(` - ${p.description} (${p.file})`)}m("")}catch(i){y.fail("Remove failed"),E(`${i instanceof Error?i.message:i}`),process.exit(1)}})};import{existsSync as sd}from"node:fs";import{mkdir as ad}from"node:fs/promises";import $t from"node:path";import oe from"picocolors";q();import{readFile as id,writeFile as nd}from"node:fs/promises";import od from"node:path";var Pa=async(e,t)=>{let r=G(t),i=od.join(e,"src","lib","config","app-apis.ts");try{let n=await id(i,"utf-8");if(n.includes(`${r}:`))return;if(!n.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let o=Mn({camelName:r,kebabName:t});n=ot(n,o),await nd(i,n)}catch(n){throw new Error(`Failed to register CRUD API endpoints: ${n}`,{cause:n})}};import va from"enquirer";var Sa=async(e,t,r,i)=>{let n=e||(await va.prompt({type:"input",name:"serviceName",message:"Service name (kebab-case):",validate:s=>s?/^[a-z0-9-]+$/.test(s)?!0:"Service name must be lowercase with hyphens only":"Service name is required"})).serviceName,o;return t!==void 0?o="axios":r!==void 0?o="fetch":i==="axios"?o="axios":i==="fetch"?o="fetch":i==="both"?o=(await va.prompt({type:"select",name:"httpClient",message:"Choose HTTP client:",choices:["axios","fetch"]})).httpClient:o="axios",{serviceName:n,httpClient:o}};var Ea=e=>{e.command("service [name]").description("Generate an API service").option("--path <path>","Custom path for service generation (default: create new feature)").option("--axios","Use Axios HTTP client").option("--fetch","Use Fetch HTTP client").option("--crud","Generate full CRUD service (getAll, getById, create, update, delete)").action(async(t,r)=>{try{let i=process.cwd();r.axios&&r.fetch&&(E("Cannot use --axios and --fetch together"),process.exit(1)),m(oe.cyan(`
1185
- \u{1F527} Service Generator
1186
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected"),n.httpClient==="none"&&(y.fail("No HTTP client is setup in this project"),E("Please setup either AxiosClient or FetchClient first"),m(oe.dim(`
1187
- Check: lib/utils/http/axios-client or lib/utils/http/fetch-client
1188
- `)),process.exit(1));let o=[];(n.httpClient==="axios"||n.httpClient==="both")&&o.push("Axios \u2713"),(n.httpClient==="fetch"||n.httpClient==="both")&&o.push("Fetch \u2713"),m(oe.dim(` HTTP Clients: ${o.join(", ")}
1189
- `));let s=await Sa(t,r.axios,r.fetch,n.httpClient);W(s.serviceName,"service name"),r.path&&pe(r.path,"path");let{basePath:c,servicePath:p}=cd(i,s.serviceName,r.path);sd(p)||await ad(p,{recursive:!0}),Ht(i,c,`${s.serviceName}.service.ts`)&&(E(`Service '${s.serviceName}' already exists at ${c}!`),process.exit(1)),r.crud?(y.start("Generating CRUD service files..."),await Jr({name:s.serviceName,outputPath:p,httpClient:s.httpClient}),y.succeed("CRUD service files generated"),y.start("Registering CRUD API endpoints..."),await Pa(i,s.serviceName),y.succeed("CRUD API endpoints registered")):await Me($i(),{projectPath:i,spinner:y,serviceName:s.serviceName,servicePath:p,httpClient:s.httpClient}),pd(s,c,!!r.crud)}catch(i){y.fail("Service generation failed"),E(`${i}`),process.exit(1)}})},cd=(e,t,r)=>{let i;if(r){let n=r.replace(/^src\//,"");if(n.startsWith("features/")){let o=n.split("/")[1];i=$t.join("src","features",o,"services")}else i=$t.join("src",n)}else i=$t.join("src","features",t,"services");return{basePath:i,servicePath:$t.join(e,i)}},pd=(e,t,r=!1)=>{let i=$t.join(t,`${e.serviceName}.service.ts`);m(oe.green(`
1190
- \u2728 ${r?"CRUD ":""}Service '${e.serviceName}' created successfully!
1191
- `)),m(oe.dim("Generated files:")),m(oe.dim(` \u{1F4C4} ${i}
1192
- `)),m(oe.cyan("Next steps:"));let n=t.replace(/^src\//,"@/");m(oe.dim(` 1. Import service: import { ${e.serviceName}Service } from '${n}/${e.serviceName}.service'`)),r?(m(oe.dim(" 2. Available methods: getAll, getById, create, update, delete")),m(oe.dim(" 3. Update DTO types in the service file"))):m(oe.dim(` 2. Use in component: const data = await ${e.serviceName}Service.getAll()`)),m("")};import ld from"enquirer";import _i from"picocolors";var{prompt:md}=ld,Fi=[{key:"http-client",label:"HTTP Client (Axios/Fetch)",flag:"httpClient",handler:or},{key:"dark-theme",label:"Dark Theme",flag:"darkTheme",handler:er},{key:"redux",label:"Redux Toolkit",flag:"redux",handler:gr},{key:"ws",label:"WebSocket Client (requires Redux)",flag:"ws",handler:wr},{key:"i18n",label:"Internationalization (next-intl)",flag:"i18n",handler:mr},{key:"tests",label:"Testing (Vitest + RTL)",flag:"tests",handler:yr},{key:"react-compiler",label:"React Compiler",flag:"reactCompiler",handler:ur},{key:"bundle-analyzer",label:"Bundle Analyzer",flag:"bundleAnalyzer",handler:Wt},{key:"security-headers",label:"Security Headers",flag:"securityHeaders",handler:hr},{key:"validate-scripts",label:"Validation Scripts",flag:"validateScripts",handler:xr},{key:"commitizen",label:"Commitizen",flag:"commitizen",handler:Xt}],dd=async(e,t={})=>{let r=t.promptFn??md,i=t.cwd??(()=>process.cwd());m(_i.cyan(`
1193
- \u{1F527} Setup Wizard
1194
- `));let n=Fi.filter(p=>!!e[p.flag]);if(n.length>0){for(let p of n)await p.handler(i());return n.length===1?n[0].key:"multiple"}let s=(await r([{type:"select",name:"feature",message:"What would you like to setup?",choices:[...Fi.map(p=>({name:p.key,message:p.label})),{name:"cancel",message:"Cancel"}]}])).feature;if(s==="cancel")return m(_i.yellow("Setup cancelled.")),null;let c=Fi.find(p=>p.key===s);return c?(await c.handler(i()),c.key):(m(_i.yellow(`
1195
- \u26A0\uFE0F ${s} setup is not implemented yet.`)),null)},Ta=e=>{e.command("setup").description("Setup features in an existing Next.js project").option("--http-client","Setup HTTP client (axios|fetch)").option("--dark-theme","Setup Dark Theme (Tailwind + @teispace/next-themes)").option("--redux","Setup Redux Toolkit").option("--ws","Setup WebSocket client (socket.io-client + Redux bridge; requires --redux)").option("--i18n","Setup next-intl for internationalization").option("--tests","Setup testing (Vitest + React Testing Library)").option("--react-compiler","Enable the React Compiler").option("--bundle-analyzer","Add @next/bundle-analyzer").option("--security-headers","Add hardened HTTP headers to next.config.ts").option("--validate-scripts","Add scripts/sync-env.ts, scripts/check-deprecated.ts, and the validate chain").option("--commitizen","Add Commitizen with the conventional-changelog adapter").action(async t=>{try{await dd(t)}catch(r){y.fail("Setup failed"),E(`${r}`),process.exit(1)}})};import{existsSync as gd}from"node:fs";import{mkdir as hd}from"node:fs/promises";import be from"node:path";import Y from"picocolors";q();import ud from"enquirer";var{prompt:fd}=ud,Ca=async(e,t)=>{let r=[];e||r.push({type:"input",name:"sliceName",message:"What is the slice name?",initial:"my-slice",validate:n=>/^[a-z0-9-]+$/.test(n)?!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 i=r.length>0?await fd(r):{};return{sliceName:e||i.sliceName,persistSlice:t===void 0?i.persistSlice||!1:t}};var Ra=e=>{e.command("slice [name]").description("Generate a Redux slice").option("--path <path>","Custom path for slice generation (default: create new feature)").option("--persist","Enable persistence for this slice").option("--no-persist","Disable persistence for this slice").option("--test","Also generate a sibling test file").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let i=process.cwd();m(Y.cyan(`
1196
- \u{1F527} Slice Generator
1197
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected"),n.hasRedux||(y.fail("Redux is not setup in this project"),E("Please install @reduxjs/toolkit and react-redux first"),m(Y.dim(`
1198
- Run: npm install @reduxjs/toolkit react-redux
1199
- `)),process.exit(1)),m(Y.dim(` Redux: \u2713
1200
- `));let o=await Ca(t,r.persist);W(o.sliceName,"slice name"),r.path&&pe(r.path,"path");let{basePath:s,slicePath:c}=yd(i,o.sliceName,r.path),p=be.join(i,s);gd(p)||await hd(p,{recursive:!0}),await Se(i,o.sliceName,s)&&(E(`Slice '${o.sliceName}' already exists at ${s}!`),process.exit(1)),await Me(ki(),{projectPath:i,spinner:y,sliceName:o.sliceName,basePath:s,slicePath:c,persistSlice:o.persistSlice});let d=null;if(xd(r,n.hasTests)){let x=be.join(c,`${o.sliceName}.slice.ts`);y.start("Generating test..."),d=await ge({projectPath:i,sourceFile:x,kind:"slice",symbolName:G(o.sliceName)}),y.succeed("Test generated")}wd(o,s,d,i)}catch(i){y.fail("Slice generation failed"),E(`${i}`),process.exit(1)}})},yd=(e,t,r)=>{let i;if(r){let n=r.replace(/^src\//,"");if(n.startsWith("features/")){let o=n.split("/")[1];i=be.join("src","features",o,"store")}else i=be.join("src",n)}else i=be.join("src","features",t,"store");return{basePath:i,slicePath:be.join(e,i,t)}},xd=(e,t)=>e.test===!1?!1:e.test===!0?!0:t,wd=(e,t,r,i)=>{let n=be.join(t,e.sliceName);m(Y.green(`
1201
- \u2728 Slice '${e.sliceName}' created successfully!
1202
- `)),m(Y.dim("Generated files:")),m(Y.dim(` \u{1F4C2} ${n}/`)),m(Y.dim(` \u251C\u2500\u2500 ${e.sliceName}.slice.ts`)),m(Y.dim(` \u251C\u2500\u2500 ${e.sliceName}.selectors.ts`)),e.persistSlice&&m(Y.dim(" \u251C\u2500\u2500 persist.ts")),m(Y.dim(` \u251C\u2500\u2500 ${e.sliceName}.types.ts`)),m(Y.dim(` \u2514\u2500\u2500 index.ts
1203
- `)),r&&m(Y.dim(` \u{1F9EA} ${be.relative(i,r)}
1204
- `)),m(Y.cyan("Next steps:"));let o=t.replace(/^src\//,"@/");m(Y.dim(` 1. Import actions: import { setLoading, setError } from '${o}/${e.sliceName}'`)),m(Y.dim(" 2. Use in component: dispatch(setLoading(true))")),m("")};import se from"node:path";import Oi from"picocolors";w();q();var ba=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
1205
- 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 i=process.cwd(),n=se.isAbsolute(t)?t:se.join(i,t);m(Oi.cyan(`
1206
- \u{1F9EA} Test Generator
1207
- `)),l(n)||(E(`Source file not found: ${t}`),process.exit(1));let o=await M(i);o.hasTests||(E("Testing is not installed in this project. Run `next-maker setup --tests` first."),process.exit(1));let s=await g(n),c=Pd(r.kind)??vd(n,s);c||(E(`Could not infer test kind for ${se.basename(n)}. Pass --kind component|hook|slice.`),process.exit(1));let p=Sd(n,c),u=Ed(n,c);l(u)&&!r.force&&(E(`Test file already exists: ${se.relative(i,u)}. Use --force to overwrite.`),process.exit(1)),y.start(`Generating ${c} test...`);let d=await ge({projectPath:i,sourceFile:n,kind:c,symbolName:p,hasRedux:o.hasRedux,hasI18n:o.hasI18n,hookUsesStore:c==="hook"&&o.hasRedux&&s.includes("useAppSelector")});y.succeed("Test generated"),m(Oi.green(`
1208
- \u2728 Test '${se.basename(d)}' created.
1209
- `)),m(Oi.dim(` \u{1F9EA} ${se.relative(i,d)}`)),m("")}catch(i){y.fail("Test generation failed"),E(`${i}`),process.exit(1)}})},Pd=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},vd=(e,t)=>{let r=se.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},Sd=(e,t)=>{let r=se.basename(e).replace(/\.(tsx?|jsx?)$/i,"");return t==="slice"?G(r.replace(/\.slice$/,"")):t==="hook"?/^use[A-Z]/.test(r)?r:`use${N(r.replace(/^use-?/,""))}`:N(r)},Ed=(e,t)=>{let r=se.dirname(e),i=se.basename(e).replace(/\.(tsx?|jsx?)$/i,""),n=t==="component"?".tsx":".ts";return se.join(r,`${i}.test${n}`)};var Ia=e=>{kn(e),Ta(e),pa(e),Ra(e),Ea(e),ya(e),da(e),po(e),fa(e),ma(e),xa(e),Gs(e),ia(e),ks(e),wa(e),ba(e)};var Cd=process.emitWarning;process.emitWarning=function(e,t,r,i){if(!(typeof e=="string"&&e.includes("--localstorage-file")&&e.includes("was provided without a valid path")))return Cd.apply(process,arguments)};async function Rd(){Li({logger:t=>Nt(t)});let e=new Td;e.name("next-maker").description("Teispace Next.js Project Generator").version(Di.version),Ia(e),e.parse()}Rd().catch(e=>{let t=e&&typeof e=="object"&&"message"in e?e.message:String(e);Nt(`Unexpected error: ${t}`)});
931
+ `,Rs=()=>`node_modules
932
+ **/node_modules
933
+ .git
934
+ .turbo
935
+ **/.next
936
+ **/coverage
937
+ **/playwright-report
938
+ **/test-results
939
+ **/.env
940
+ **/.env.*
941
+ `,js=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)))},Cs=(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},Ts=e=>Object.keys(e).length?`
942
+ catalog:
943
+ ${Object.entries(e).map(([t,r])=>` '${t}': '${r}'`).join(`
944
+ `)}
945
+ `:"";var cp=(e,t=[])=>[...t,e],Os={hooks:!1,commitizen:!1,ci:!1,docker:!1,githubTemplates:!1,communityFiles:[]},As=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 (${Ie().join(", ")})`).option("--set <key=value>","Starter option applied to every app (repeatable)",cp).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 lp(t,r)}catch(n){x(`${n instanceof Error?n.message:n}`),process.exit(1)}})},je=async(e,t,r,n)=>{let o=$t(e);n.current=o;try{let s=await r();return o.succeed(t),s}finally{n.current===o&&(n.current=null)}},pp=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)et(r);if(new Set(t).size!==t.length)throw new Error("App names must be unique.");return t},lp=async(e,t)=>{vt(),et(e);let r=pp(t.apps??"web,admin"),n=v.resolve(process.cwd(),e);if(y(n))throw new Error(`Directory ${e} already exists.`);let o={...t.preset?Ne(t.preset):{},...ye(t.set)};for(let[p,m]of Object.entries(Os))o[p]!==void 0&&JSON.stringify(o[p])!==JSON.stringify(m)&&l(fe.yellow(` ! ${p} is managed at the workspace root; ignoring --set ${p}`)),o[p]=m;o.packageManager="pnpm";let s={current:null},i=async()=>{y(n)&&(s.current?.stop(),s.current=null,l(fe.yellow(`
946
+ Cleaning up ${e}...`)),await Ze(n))},a=xt(async()=>{l(fe.red(`
947
+ Process interrupted.`)),await i()});try{await qt(v.join(n,"apps"),{recursive:!0}),await qt(v.join(n,"packages"),{recursive:!0});let p=B({starterPath:t.starterPath}),m=v.join(n,"apps",r[0]),c=await je(`Fetching starter (${p.location})...`,"Starter fetched.",()=>xe({projectPath:m,source:p,supplied:o,packageManager:"pnpm"}),s);if(!t.yes){let b=await Rt(c.manifest,c.answers);c=tt(c.manifest,{...b,...Os,packageManager:"pnpm"},"pnpm"),l("")}for(let b of c.forced)l(fe.yellow(` ! ${b.reason}; ${b.option} turned off`));let d=JSON.parse(await Jt(v.join(m,"package.json"),"utf-8")),u=await Jt(v.join(m,"pnpm-workspace.yaml"),"utf-8"),f={};for(let b of[".nvmrc",".npmrc",".editorconfig"])y(v.join(m,b))&&(f[b]=await Jt(v.join(m,b),"utf-8"));let h=b=>le(b,{projectName:b,description:`${b} app of ${e}`,readme:!0,copyEnv:!0});await je(`Composing apps/${r[0]}...`,`apps/${r[0]} composed.`,()=>be(m,h(r[0]),c,p),s);for(let b of r.slice(1)){let S=v.join(n,"apps",b);await je(`Composing apps/${b}...`,`apps/${b} composed.`,async()=>{let R=await xe({projectPath:S,source:p,supplied:c.answers,packageManager:"pnpm"});await be(S,h(b),R,p)},s)}let w=[];for(let b of r){let S=v.join(n,"apps",b);for(let D of["pnpm-workspace.yaml","pnpm-lock.yaml",".npmrc",".nvmrc"])await ap(v.join(S,D),{force:!0});let R=JSON.parse(await Jt(v.join(S,"package.json"),"utf-8"));delete R.packageManager,delete R.engines,w.push(R)}let P=t.catalog===!1?{}:js(w);for(let[b,S]of r.entries()){let R=Cs(w[b],P);await I(v.join(n,"apps",S,"package.json"),`${JSON.stringify(R,null,2)}
948
+ `),t.docker&&await I(v.join(n,"apps",S,"Dockerfile"),Ss(S))}await je("Writing workspace root...","Workspace root written.",async()=>{let b={name:e,description:`${e} workspace`,author:d.author??"",apps:r,packageManager:"pnpm",packageManagerPin:d.packageManager??"pnpm@11.25.0",nodeEngine:d.engines?.node??">=24.0.0",turboVersion:"^2.10.0",biomeVersion:d.devDependencies?.["@biomejs/biome"]??"^2.5.0",hooks:t.hooks!==!1,huskyVersion:d.devDependencies?.husky,lintStagedVersion:d.devDependencies?.["lint-staged"],commitlintCliVersion:d.devDependencies?.["@commitlint/cli"],commitlintConfigVersion:d.devDependencies?.["@commitlint/config-conventional"]};await I(v.join(n,"package.json"),gs(b)),await I(v.join(n,"pnpm-workspace.yaml"),hs(u)+Ts(P)),await I(v.join(n,"turbo.json"),ys()),await I(v.join(n,"biome.json"),ws(b.biomeVersion)),await I(v.join(n,".gitignore"),xs()),await I(v.join(n,"README.md"),bs(b,c.manifest.starter.version)),await I(v.join(n,"packages",".gitkeep"),"");for(let[S,R]of Object.entries(f))await I(v.join(n,S),R);if(t.docker&&(await I(v.join(n,"docker-compose.yml"),Es(r)),await I(v.join(n,".dockerignore"),Rs())),t.ci!==!1&&(await qt(v.join(n,".github","workflows"),{recursive:!0}),await I(v.join(n,".github","workflows","ci.yml"),vs())),t.hooks!==!1){await I(v.join(n,".lintstagedrc.mjs"),$s()),await I(v.join(n,"commitlint.config.mjs"),Ps()),await qt(v.join(n,".husky"),{recursive:!0});for(let[S,R]of Object.entries(ks())){let D=v.join(n,".husky",S);await I(D,R),await sp(D,493)}}await I(v.join(n,".next-maker.json"),`${JSON.stringify({workspace:!0,apps:r,starter:c.manifest.starter,answers:c.answers,docker:!!t.docker,catalog:t.catalog!==!1},null,2)}
949
+ `)},s),t.install!==!1&&(await je("Installing dependencies with pnpm...","Dependencies installed.",()=>pe(n,"pnpm"),s),await je("Formatting...","Formatted.",()=>Et(n,"pnpm","lint:fix"),s));for(let b of r){let S=v.join(n,"apps",b,".env.example");y(S)&&await ip(S,v.join(n,"apps",b,".env"))}t.git!==!1&&await je("Initializing git...","Git initialized.",()=>St(n),s),a(),l(""),l(fe.green(`\u2728 Workspace ${e} is ready with ${r.map(b=>`apps/${b}`).join(", ")}.`)),l(""),l("Next:"),l(fe.cyan(` cd ${e}`)),t.install===!1&&l(fe.cyan(" pnpm install")),l(fe.cyan(` pnpm --filter ${r[0]} dev`)),l("")}catch(p){throw a(),s.current?.fail("Failed to create workspace."),s.current=null,await i(),p}};var Is=e=>{Tn(e),As(e),ms(e),Qo(e),ls(e),zo(e),us(e),ns(e),es(e),Ko(e),Po(e),Yo(e),Ho(e),ts(e),Eo(e),Vo(e),ko(e),rs(e),fs(e)};var dp=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 dp.apply(process,arguments)};async function up(){an({logger:t=>bt(t)});let e=new mp;e.name("next-maker").description("Teispace Next.js Project Generator").version(Te.version),Is(e),e.parse()}up().catch(e=>{let t=e&&typeof e=="object"&&"message"in e?e.message:String(e);bt(`Unexpected error: ${t}`)});