@teispace/next-maker 4.0.2 → 5.0.1

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 +788 -1045
  4. package/package.json +9 -4
package/dist/index.js CHANGED
@@ -1,732 +1,537 @@
1
1
  #!/usr/bin/env node
2
- var Na=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)Na(e,r,{get:t[r],enumerable:!0})};import{existsSync as br}from"node:fs";import Ie from"node:fs/promises";import Ui 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(Ui.dirname(e),{recursive:!0}),await Ie.writeFile(e,t,"utf-8")},b=async(e,t)=>{await Ie.mkdir(Ui.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 Xi={};Rr(Xi,{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 nn={};Rr(nn,{cleanupHttpTypes:()=>Ue,injectBundleSentinel:()=>rn,installBundleSentinelMount:()=>tt,isFileUsed:()=>dc,isStringUsed:()=>uc,migrateClientUsages:()=>jr,removeBundleSentinelMount:()=>rt,removeHttpExports:()=>Mr,resolveBundleSentinelLayoutPath:()=>Lr,rewriteSentinelImports:()=>we,rewriteServerEntryImports:()=>Pe,stripBundleSentinel:()=>_t,stripSaveAuthTokens:()=>Dr,updateConfigIndex:()=>Or,updateHttpIndex:()=>Ar,updateTypesIndex:()=>Fr,updateUtilsIndex:()=>_r});import{execFile as cc}from"node:child_process";import D from"node:path";import{promisify as pc}from"node:util";async function Nr(e,t,r){try{let{stdout:i}=await lc("grep",[...r,"--include=*.ts","--include=*.tsx",e,t]);return i}catch(i){if(i.code===1)return"";throw i}}var lc,Ar,_r,Fr,Or,Ue,Dr,jr,Lr,mc,Qi,en,rn,_t,tt,rt,Mr,dc,uc,we,Pe,tn,Be=Ge(()=>{"use strict";P();w();lc=pc(cc);Ar=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)}},_r=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))}},Fr=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)}},Or=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,`
9
-
10
- `).trim()}
11
- `,await h(r,i))}},Dr=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},jr=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 Nr(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)}},Lr=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},mc="// Regression sentinel \u2014 see file comment for what this guards.",Qi="import { HttpClientBundleSentinel } from '@/lib/utils/http/__bundle-sentinel__/client-bundle-sentinel';",en="<HttpClientBundleSentinel />",rn=e=>{if(e.includes(en))return e;let t=e;if(!t.includes(Qi)){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}${mc}
13
- ${Qi}
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}${en}
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=Lr(e);if(!t)return;let r=await g(t),i=rn(r);i!==r&&await h(t,i)},rt=async e=>{let t=Lr(e);if(!t)return;let r=await g(t),i=_t(r);i!==r&&await h(t,i)},Mr=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)}},dc=async(e,t,r=[])=>{let i=D.join(e,"src"),o=`from ['"].*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}.*['"]`;try{let s=await Nr(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}},uc=async(e,t,r=[])=>{let i=D.join(e,"src");try{let n=await Nr(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=tn(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=tn(i,"createFetchClient")),i=i.replace(/\n{3,}/g,`
25
-
26
- `),await h(r,i)},tn=(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 op}from"node:fs";import{access as sp}from"node:fs/promises";import Fn from"node:path";var Se,Ht,On=Ge(()=>{"use strict";Se=async(e,t,r)=>{try{return await sp(Fn.join(e,r,t)),!0}catch{return!1}},Ht=(e,t,r)=>op(Fn.join(e,t,r))});import{access as Ut,readFile as ap}from"node:fs/promises";import at from"node:path";var M,Dn=Ge(()=>{"use strict";M=async e=>{try{let t=at.join(e,"package.json"),r=await ap(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";On();Dn()});import{Command as Cd}from"commander";var ji={name:"@teispace/next-maker",version:"4.0.2",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 Ur from"node:path";import ee from"picocolors";var Ze=new Set;function Li(e){return Ze.add(e),()=>Ze.delete(e)}function Mi(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 _a={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=_a[t]??(i=>i);console.log(r(e))}function Nt(e){xe(`\u2716 ${e}`,"red")}var E=Nt;function Gi(e){xe(`\u26A0 ${e}`,"yellow")}function m(e){xe(e)}function Hi(){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 Fa from"ora";function S(e="",t){let r=Fa({text:e,...t});return r.start(),r}var Oa=e=>e.charAt(0).toUpperCase()+e.slice(1),G=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),N=e=>Oa(G(e));import Da from"ora";var y=Da();w();import{execFile as ja}from"node:child_process";import{promisify as La}from"node:util";var Ma=La(ja),ue=(e,t)=>Ma("git",e,t?{cwd:t}:void 0);function Ga(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 Bi=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&&(Ga(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 Ha}from"node:child_process";import{promisify as Ua}from"node:util";var Ba=Ua(Ha),Qe=(e,t,r)=>Ba(e,t,{cwd:r,maxBuffer:64*1024*1024}),Ir=e=>{let t=e;return(`${t.stdout??""}
2
+ import{Command as Ip}from"commander";var Ae={name:"@teispace/next-maker",version:"5.0.1",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 Qi}from"node:fs/promises";import At from"node:path";import I from"picocolors";var on=/@next-maker:([\w-]+)(?::(start|end))?/,zs=/^\s*(?:\{\s*\/\*.*?\*\/\s*\}|\/\*.*?\*\/|\/\/.*|#.*)\s*$/,Ws=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]*$/,""),Xt=(e,{off:t})=>{let r=e.split(`
3
+ `),n=[];for(let o=0;o<r.length;o++){let s=r[o],i=s.match(on);if(!i){n.push(s);continue}let[,a,c]=i,l=t.has(a);if(c==="start"){let u=Ks(r,o+1,a);l&&(o=u);continue}if(c==="end")continue;let p=zs.test(s);if(l){p&&o++;continue}p||n.push(Ws(s))}return n.join(`
4
+ `)},Ks=(e,t,r)=>{for(let n=t;n<e.length;n++){let o=e[n].match(on);if(o&&o[1]===r&&o[2]==="end")return n}throw new Error(`Anchor @next-maker:${r}:start has no matching :end`)},ze=e=>e.includes("@next-maker:"),sn=(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 c=a[2].split(",").map(p=>p.trim()).filter(Boolean),l=c.filter(p=>p.replace(/^type\s+/,"").split(/\s+as\s+/)[0]!==t);if(l.length===0)continue;if(l.length<c.length){n.push(`${a[1]}{ ${l.join(", ")} }${a[3]}`);continue}}n.push(i)}return n.join(`
6
+ `)},We=(e,t)=>{let r=e.split(`
7
+ `),n=new RegExp(`^([ \\t]*)<${t}\\b`),o=-1,s="";for(let f=0;f<r.length;f++){let d=r[f].match(n);if(d){o=f,s=d[1];break}}if(o===-1)return e;let i=o;for(;i<r.length&&!/>\s*$/.test(r[i]);)i++;if(i>=r.length)throw new Error(`unwrapJsx: opening <${t}> never closes`);if(/\/>\s*$/.test(r[i]))return e;let a=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),c=new RegExp(`^${a}</${t}>\\s*$`),l=-1;for(let f=i+1;f<r.length;f++)if(c.test(r[f])){l=f;break}if(l===-1)throw new Error(`unwrapJsx: no closing </${t}> at the opening indent`);let p=r.slice(i+1,l).map(f=>f.startsWith(" ")?f.slice(2):f),u=[...r.slice(0,o),...p,...r.slice(l+1)].join(`
8
+ `);return sn(u,t)},Ke=(e,t)=>{let r=new RegExp(`\\b${t}\\(`,"g"),n=e,o=r.exec(n);for(;o;){let s=o.index,i=0,a=s+o[0].length-1;for(;a<n.length;a++)if(n[a]==="(")i++;else if(n[a]===")"&&(i--,i===0))break;if(i!==0)throw new Error(`unwrapCall: unbalanced parentheses after ${t}(`);let c=n.slice(s+o[0].length,a);n=n.slice(0,s)+c+n.slice(a+1),r.lastIndex=s,o=r.exec(n)}return n};import{cp as dn,mkdir as ni,mkdtemp as oi,readdir as mn,readFile as Ie,rm as Qt,rmdir as si,stat as ii,writeFile as Ne}from"node:fs/promises";import{tmpdir as ai}from"node:os";import F from"node:path";import{readdir as Xs}from"node:fs/promises";import wt from"node:path";var an=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}$`)},Ys=new Set(["node_modules",".git",".next","coverage","playwright-report","test-results"]),L=async(e,t=Ys)=>{let r=[],n=async o=>{let s;try{s=await Xs(o,{withFileTypes:!0})}catch{return}for(let i of s)if(i.isDirectory()){if(t.has(i.name))continue;await n(wt.join(o,i.name))}else i.isFile()&&r.push(wt.relative(e,wt.join(o,i.name)).split(wt.sep).join("/"))};return await n(e),r.sort()},ie=(e,t)=>{let r=an(t);return e.filter(n=>r.test(n))};import{readFile as Qs}from"node:fs/promises";import Zs from"node:path";var xt="next-maker.json",ye=".next-maker/overlays",cn=1,B=e=>{throw new Error(`Invalid ${xt}: ${e}`)},ln=e=>{(typeof e!="object"||e===null)&&B("not an object");let t=e;t.manifestVersion!==cn&&B(`manifestVersion ${String(t.manifestVersion)} is not supported by this CLI (expected ${cn})`),(!t.starter?.name||!t.starter.version)&&B("starter.name and starter.version are required"),(!t.options||typeof t.options!="object")&&B("options is required"),(!t.features||typeof t.features!="object")&&B("features is required"),(!t.packageManagers||typeof t.packageManagers!="object")&&B("packageManagers is required");let r=t.options,n=t.features;for(let[o,s]of Object.entries(r))["choice","boolean","multi"].includes(s.type)||B(`option ${o} has unknown type ${String(s.type)}`),s.type!=="boolean"&&!Array.isArray(s.values)&&B(`option ${o} needs a values array`),s.default===void 0&&B(`option ${o} needs a default`);for(let[o,s]of Object.entries(n)){(!s.when||typeof s.when!="object")&&B(`feature ${o} needs a when`);for(let i of Object.keys(s.when))r[i]||B(`feature ${o} refers to unknown option ${i}`)}return{...t,always:{remove:t.always?.remove??[]}}},Xe=async e=>{let t=Zs.join(e,xt),r;try{r=await Qs(t,"utf-8")}catch{throw new Error(`The starter at ${e} has no ${xt}. This CLI needs a starter that ships a composition manifest (nextjs-starter 2.x or later).`)}return ln(JSON.parse(r))},pn=(e,t)=>e.some(r=>r===t),bt=(e,t,r)=>t===void 0?!1:e?.type==="multi"&&Array.isArray(t)?t.some(n=>pn(r,n)):pn(r,t),ae=(e,t,r)=>Object.entries(t.when).every(([n,o])=>bt(e.options[n],r[n],o)),ei=e=>e.type==="boolean"?!1:e.type==="multi"?[]:e.default,ti=(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},Ye=(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:ti(i,s,a)}for(let[s,i]of Object.entries(e.options))if(i.requires)for(let[a,c]of Object.entries(i.requires)){if(bt(e.options[a],r[a],c))continue;let l=ei(i);r[s]!==l&&JSON.stringify(r[s])!==JSON.stringify(l)&&(n.push({option:s,value:l,reason:`${s} requires ${a} to be ${c.map(String).join(" or ")}`}),r[s]=l)}return{answers:r,forced:n,unknown:o}};var ce=(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}`}},ri=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[]}},vt=(e,t)=>{if(t==="pnpm")return e;let r=e;for(let[n,o]of ri(t))r=r.replace(n,o);return r},Yt=e=>e==="package.json"||e.startsWith(".husky/")||e.endsWith(".md")||e===".lintstagedrc.mjs";var we=e=>[...new Set(e)],er=(e,t,r)=>{let n=e.packageManagers[r];if(!n)throw new Error(`This starter does not support ${r}; valid: ${Object.keys(e.packageManagers).join(", ")}`);let o=[],s=[],i=[],a=[],c=[];for(let[p,u]of Object.entries(e.features)){let f=ae(e,u,t);(f?o:s).push(p);let d=f?u.on:u.off;if(!d)continue;i.push(d);let g=Object.keys(u.when);d.overlay&&!a.some(y=>y.name===d.overlay)&&a.push({name:d.overlay,options:g}),d.remove?.length&&c.push({feature:p,options:g,patterns:d.remove})}let l=we([...e.always.remove,...n.remove??[]]);return{answers:t,packageManager:r,pmSpec:n,featuresOn:o,featuresOff:s,pmOverlays:n.overlay?[n.overlay]:[],featureOverlays:a,removePatterns:we([...l,...c.flatMap(p=>p.patterns)]),unconditionalRemovals:l,featureRemovals:c,anchorsOff:new Set(i.flatMap(p=>p.anchors??[])),unwrapJsx:i.flatMap(p=>p.unwrapJsx??[]),unwrapCall:i.flatMap(p=>p.unwrapCall??[]),packages:we(i.flatMap(p=>p.packages??[])),devPackages:we(i.flatMap(p=>p.devPackages??[])),scripts:we(i.flatMap(p=>p.scripts??[])),env:we(i.flatMap(p=>p.env??[])),packageJsonKeys:we(i.flatMap(p=>p.packageJsonKeys??[]))}},ci=new Set([".ts",".tsx",".js",".mjs",".cjs",".css",".json",".yml",".yaml",".md",".sh",".example",".mdx"]),pi=e=>ci.has(F.extname(e))||e.startsWith(".husky/")||e==="Dockerfile",un=async(e,t,r)=>{let n=F.join(e,r),o=await L(n,new Set);if(o.length===0)throw new Error(`Overlay "${r}" is missing or empty at ${ye}/${r}`);for(let s of o){let i=F.join(t,s);await ni(F.dirname(i),{recursive:!0}),await dn(F.join(n,s),i,{force:!0})}return o},tr=async(e,t=e)=>{let r;try{r=await mn(t,{withFileTypes:!0})}catch{return}for(let n of r)n.isDirectory()&&n.name!=="node_modules"&&n.name!==".git"&&await tr(e,F.join(t,n.name));t!==e&&(await mn(t)).length===0&&await si(t)},fn=(e,t)=>{let r=t.split("."),n=e;for(let s of r.slice(0,-1)){let i=n?.[s];if(typeof i!="object"||i===null)return;n=i}let o=r[r.length-1];if(n&&o in n&&delete n[o],r.length>1){let s=r.slice(0,-1).join("."),i=r.slice(0,-1).reduce((a,c)=>a?.[c],e);i&&typeof i=="object"&&Object.keys(i).length===0&&fn(e,s)}},li=async(e,t,r)=>{let n=F.join(e,"package.json"),o=JSON.parse(await Ie(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)fn(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=>ce(t.packageManager,a)).join(" && ")}if(o.scripts)for(let[i,a]of Object.entries(o.scripts))typeof a=="string"&&(o.scripts[i]=vt(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 Ne(n,`${JSON.stringify(o,null,2)}
9
+ `,"utf-8")},mi=async(e,t)=>{let r=F.join(e,".env.example"),n;try{n=await Ie(r,"utf-8")}catch{return}for(let o of t)n=n.replace(new RegExp(`^${o}=.*\\n?`,"gm"),"");await Ne(r,n.replace(/\n{3,}/g,`
10
+
11
+ `),"utf-8")},rr=async(e,t,r)=>{let n={removed:[],overlaid:[],anchored:[],unwrapped:[]},o=F.join(e,ye),s=await oi(F.join(ai(),"next-maker-overlays-"));try{await Zt(o)&&await dn(o,s,{recursive:!0});for(let l of r.pmOverlays)n.overlaid.push(...await un(s,e,l));let a=await L(e),c=new Set(r.removePatterns.flatMap(l=>ie(a,l)));for(let l of c)await Qt(F.join(e,l),{force:!0}),n.removed.push(l);for(let l of r.featureOverlays){let p=await un(s,e,l.name),u=[...r.unconditionalRemovals,...r.featureRemovals.filter(d=>!d.options.some(g=>l.options.includes(g))).flatMap(d=>d.patterns)],f=new Set(u.flatMap(d=>ie(p,d)));for(let d of f)await Qt(F.join(e,d),{force:!0}),n.removed.push(d);n.overlaid.push(...p.filter(d=>!f.has(d)))}}finally{await Qt(s,{recursive:!0,force:!0})}await tr(e);let i=await L(e);for(let a of i){if(!pi(a))continue;let c=F.join(e,a),l=await Ie(c,"utf-8");ze(l)&&(await Ne(c,Xt(l,{off:r.anchorsOff}),"utf-8"),n.anchored.push(a))}for(let{file:a,tag:c}of r.unwrapJsx){let l=F.join(e,a);if(!await Zt(l))continue;let p=await Ie(l,"utf-8"),u=We(p,c);u!==p&&(await Ne(l,u,"utf-8"),n.unwrapped.push(`${a}:<${c}>`))}for(let{file:a,name:c}of r.unwrapCall){let l=F.join(e,a);if(!await Zt(l))continue;let p=await Ie(l,"utf-8"),u=Ke(p,c);u!==p&&(await Ne(l,u,"utf-8"),n.unwrapped.push(`${a}:${c}()`))}if(await li(e,r,t),await mi(e,r.env),r.packageManager!=="pnpm")for(let a of await L(e)){if(!Yt(a)||a==="package.json")continue;let c=F.join(e,a),l=await Ie(c,"utf-8"),p=vt(l,r.packageManager);p!==l&&await Ne(c,p,"utf-8")}return n},Zt=async e=>{try{return await ii(e),!0}catch{return!1}};import{readFile as ui}from"node:fs/promises";import di from"node:path";var nr=async e=>{let t=di.resolve(process.cwd(),e),r;try{r=await ui(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},xe=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 or={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}}},Fe=()=>Object.keys(or),Me=e=>{let t=or[e];if(!t)throw new Error(`Unknown preset "${e}". Valid: ${Fe().join(", ")}.`);return{...t.answers}};var Qe=new Set;function $t(e){return Qe.add(e),()=>Qe.delete(e)}function gn(e){let{logger:t=c=>console.error(c),exitOnCancel:r=process.env.NODE_ENV!=="test"}=e??{},n=!1,o=async()=>{let c=[...Qe];Qe.clear();for(let l of c)try{await l()}catch(p){t(`Cleanup failed: ${p instanceof Error?p.message:String(p)}`)}},s=()=>{if(n){r&&process.exit(130);return}n=!0;let c=Qe.size>0;o().then(()=>{console.log(""),t("Setup cancelled by user"),console.log(""),r&&process.exit(c?130:0)})},i=c=>{if(c!==null&&typeof c=="object"&&"code"in c&&c.code==="ERR_USE_AFTER_CLOSE"){s();return}if(c instanceof Error){t(`Uncaught exception: ${c.message}`);return}t(`Uncaught exception: ${String(c)}`)},a=c=>{if(c instanceof Error){t(`Unhandled promise rejection: ${c.message}`);return}t(`Unhandled promise rejection: ${String(c)}`)};return process.on("SIGINT",s),process.on("SIGTERM",s),process.on("uncaughtException",i),process.on("unhandledRejection",a),()=>{process.off("SIGINT",s),process.off("SIGTERM",s),process.off("uncaughtException",i),process.off("unhandledRejection",a)}}import z from"picocolors";var fi={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 pe(e,t="reset"){let r=fi[t]??(n=>n);console.log(r(e))}function kt(e){pe(`\u2716 ${e}`,"red")}var b=kt;function hn(e){pe(`\u26A0 ${e}`,"yellow")}function m(e){pe(e)}function Pt(){console.log(""),pe("\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"),pe("\u2551 \u2551","cyan"),pe("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),pe("\u2551 \u2551","cyan"),pe("\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 gi from"ora";function St(e="",t){let r=gi({text:e,...t});return r.start(),r}var hi=e=>e.charAt(0).toUpperCase()+e.slice(1),G=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),$=e=>hi(G(e));import yi from"ora";var h=yi();import{execFile as wi}from"node:child_process";import{cp as yn,mkdir as xi,stat as bi}from"node:fs/promises";import Ze from"node:path";import{promisify as vi}from"node:util";import $i from"degit";var ki=vi(wi),wn="teispace/nextjs-starter",Et="v2.0.0-alpha.1",Pi="NEXT_MAKER_STARTER_PATH",Si=(e=Et)=>e==="main"?wn:`${wn}#${e}`,V=(e={})=>{let t=e.starterPath??process.env[Pi];return t?{kind:"local",location:Ze.resolve(t)}:{kind:"remote",location:Si(e.ref)}},Ei=new Set(["node_modules",".git",".next","coverage","playwright-report","test-results",".env"]),Tt=async(e,t={})=>{let r=t.source??V();if(r.kind==="local"){try{if(!(await bi(r.location)).isDirectory())throw new Error("not a directory")}catch{throw new Error(`Starter path ${r.location} is not a directory`)}return await Ti(r.location,e),r}return await $i(r.location,{cache:!1,force:!0,verbose:t.verbose??!1}).clone(e),r},Ti=async(e,t)=>{let r=null;try{let{stdout:n}=await ki("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 yn(e,t,{recursive:!0,filter:n=>!Ei.has(Ze.basename(n))});return}for(let n of r){let o=Ze.join(t,n);await xi(Ze.dirname(o),{recursive:!0});try{await yn(Ze.join(e,n),o)}catch{}}};import{existsSync as sr}from"node:fs";import et from"node:fs/promises";import Ri from"node:path";var j=async e=>et.readFile(e,"utf-8"),P=async(e,t)=>{await et.mkdir(Ri.dirname(e),{recursive:!0}),await et.writeFile(e,t,"utf-8")};var xn=async e=>{sr(e)&&await et.unlink(e)},tt=async e=>{sr(e)&&await et.rm(e,{recursive:!0,force:!0})},bn=async(e,t)=>{let r=await j(e),n=JSON.parse(r),o=t(n);await P(e,JSON.stringify(o,null,2))},w=e=>sr(e);import{execFile as ji}from"node:child_process";import{promisify as Ci}from"node:util";var Oi=Ci(ji),te=(e,t)=>Oi("git",e,t?{cwd:t}:void 0);function Ai(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 Rt=async(e,t,r)=>{try{if(await te(["init"],e),await te(["add","."],e),await te(["commit","-m","Initial commit from @teispace/next-maker"],e),t&&(Ai(t),await te(["remote","add","origin",t],e),r)){await te(["fetch","origin"],e);let n="main";try{await te(["show-branch","origin/main"],e)}catch{try{await te(["show-branch","origin/master"],e),n="master"}catch{n="main"}}await te(["merge",`origin/${n}`,"--allow-unrelated-histories","-X","ours"],e),await te(["push","origin",`HEAD:${n}`],e)}}catch(n){console.warn("Failed to initialize git repository",n)}};import{execFile as Ii}from"node:child_process";import{promisify as Ni}from"node:util";var Fi=Ni(Ii),vn=(e,t,r)=>Fi(e,t,{cwd:r,maxBuffer:64*1024*1024}),Mi=e=>{let t=e;return(`${t.stdout??""}
28
12
  ${t.stderr??""}`.trim()||(t.message??String(e))).split(`
29
13
  `).filter(Boolean).slice(-12).join(`
30
- `)},zi=async(e,t)=>{try{await Qe(t,["install"],e)}catch(r){throw new Error(`Failed to install dependencies with ${t}:
31
- ${Ir(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}`)}},za=()=>{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":za()},$r=async(e,t,r)=>{if(r.length!==0)try{await Qe(t,[...Vi(t),...r],e)}catch(i){throw new Error(`Failed to install packages with ${t}:
32
- ${Ir(i)}`,{cause:i})}},Vi=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 $r(e,r,[t])},Va=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,[...Vi(t),Va(t),...r],e)}catch(i){throw new Error(`Failed to install dev packages with ${t}:
33
- ${Ir(i)}`,{cause:i})}},At=async(e,t)=>{let r=await A(e);await fe(e,r,[t])},kr=async(e,t,r)=>{if(r.length!==0)try{await Qe(t,[...Ja(t),...r],e)}catch(i){throw new Error(`Failed to uninstall packages with ${t}: ${i}`,{cause:i})}},Ja=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 kr(e,r,[t])};import Wa from"enquirer";var qa=/^[a-z0-9-_]+$/,Xa=/^\d+\.\d+\.\d+$/,Ji=e=>qa.test(e),Ya=e=>Xa.test(e),Wi=(e,t={})=>{let r=t.projectName??e??"my-app";if(!Ji(r))throw new Error(`Invalid project name "${r}". Use lowercase letters, digits, hyphens, and underscores only.`);let i=t.version??"0.1.0";if(!Ya(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:Ka}=Wa,qi=async e=>{if(e!==void 0&&!Ji(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 Ka([{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 Za from"node:path";var ce=async(e,t)=>{let r=Qa(t);await h(Za.join(e,"test/test-utils.tsx"),r)},Qa=({redux:e,i18n:t})=>e&&t?ec:e?tc:t?rc:ic,ec=`import { type RenderOptions, render } from '@testing-library/react';
34
- import { NextIntlClientProvider } from 'next-intl';
35
- import type { ReactElement, ReactNode } from 'react';
36
- import { Provider } from 'react-redux';
37
- import { type AppState, type AppStore, makeStore } from '@/store';
38
- import type { SupportedLocale } from '@/types/i18n';
39
-
40
- type TestProvidersProps = {
41
- children: ReactNode;
42
- store?: AppStore;
43
- preloadedState?: Partial<AppState>;
44
- messages?: Record<string, unknown>;
45
- locale?: SupportedLocale;
46
- };
47
-
48
- export function TestProviders({
49
- children,
50
- store,
51
- preloadedState,
52
- messages = {},
53
- locale = 'en',
54
- }: TestProvidersProps) {
55
- const testStore = store ?? makeStore(preloadedState);
56
-
57
- return (
58
- <Provider store={testStore}>
59
- <NextIntlClientProvider locale={locale} messages={messages} timeZone="UTC">
60
- {children}
61
- </NextIntlClientProvider>
62
- </Provider>
63
- );
64
- }
65
-
66
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'> & {
67
- store?: AppStore;
68
- preloadedState?: Partial<AppState>;
69
- messages?: Record<string, unknown>;
70
- locale?: SupportedLocale;
71
- };
72
-
73
- export function renderWithProviders(
74
- ui: ReactElement,
75
- { store, preloadedState, messages, locale, ...renderOptions }: ExtendedRenderOptions = {},
76
- ) {
77
- const testStore = store ?? makeStore(preloadedState);
78
-
79
- return {
80
- store: testStore,
81
- ...render(ui, {
82
- wrapper: ({ children }) => (
83
- <TestProviders store={testStore} messages={messages} locale={locale}>
84
- {children}
85
- </TestProviders>
86
- ),
87
- ...renderOptions,
88
- }),
89
- };
90
- }
91
-
92
- export * from '@testing-library/react';
93
- export { default as userEvent } from '@testing-library/user-event';
94
- `,tc=`import { type RenderOptions, render } from '@testing-library/react';
95
- import type { ReactElement, ReactNode } from 'react';
96
- import { Provider } from 'react-redux';
97
- import { type AppState, type AppStore, makeStore } from '@/store';
98
-
99
- type TestProvidersProps = {
100
- children: ReactNode;
101
- store?: AppStore;
102
- preloadedState?: Partial<AppState>;
103
- };
104
-
105
- export function TestProviders({ children, store, preloadedState }: TestProvidersProps) {
106
- const testStore = store ?? makeStore(preloadedState);
107
-
108
- return <Provider store={testStore}>{children}</Provider>;
109
- }
110
-
111
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'> & {
112
- store?: AppStore;
113
- preloadedState?: Partial<AppState>;
114
- };
14
+ `)},_i=e=>{switch(e){case"pnpm":return["install","--no-frozen-lockfile"];case"yarn":return["install","--no-immutable"];default:return["install"]}},le=async(e,t)=>{try{await vn(t,_i(t),e)}catch(r){throw new Error(`Failed to install dependencies with ${t}:
15
+ ${Mi(r)}`,{cause:r})}},jt=async(e,t,r)=>{let n=t==="npm"?["run",r]:[r];try{await vn(t,n,e)}catch(o){console.warn(`Warning: Failed to run script '${r}': ${o}`)}};import Di from"enquirer";var{prompt:$n}=Di,Li=/^[a-z0-9-_]+$/,Ui=/^\d+\.\d+\.\d+$/,kn=e=>Li.test(e),Pn=e=>Ui.test(e),rt=e=>{if(!kn(e))throw new Error(`Invalid project name "${e}". Use lowercase letters, digits, hyphens, and underscores only \u2014 no slashes, "..", or absolute paths.`)},me=(e,t={})=>{let r=t.projectName??e??"my-app";rt(r);let n=t.version??"0.1.0";if(!Pn(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}},Sn=e=>e.state?.answers??e.enquirer?.answers??{},En=async e=>{e!==void 0&&rt(e);let t=await $n([{type:"input",name:"projectName",message:"What is the project name?",initial:e||"my-app",skip:!!e,validate:r=>kn(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=>Pn(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!Sn(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 me(e,{...t,projectName:e??t.projectName})},Ct=async(e,t={})=>{let r=Object.entries(e.options).map(([s,i])=>{let a=t[s]??i.default,c={name:s,message:i.label??s,skip:function(){if(!i.requires)return!1;let p=Sn(this);return!Object.entries(i.requires).every(([u,f])=>bt(e.options[u],p[u],f))}};if(i.type==="boolean")return{...c,type:"confirm",initial:!!a};if(i.type==="multi")return{...c,type:"multiselect",choices:(i.values??[]).map(p=>({name:p,value:p})),initial:Array.isArray(a)?a:[]};let l=i.values??[];return{...c,type:"select",choices:l,initial:Math.max(0,l.indexOf(String(a)))}}),n=await $n(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 Gi 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 Bi=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}},Tn=async(e,t)=>{let{remote:r,homepage:n,issues:o}=Bi(t);await bn(Gi.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 Ki}from"node:fs/promises";import de from"node:path";import{cp as Vi,mkdir as Hi,mkdtemp as qi,readFile as be,rm as Cn,writeFile as Ot}from"node:fs/promises";import{tmpdir as Ji}from"node:os";import A from"node:path";var _e=".next-maker.json",ue=async(e,t)=>{await Ot(A.join(e,_e),`${JSON.stringify(t,null,2)}
16
+ `,"utf-8")},re=async e=>{let t=A.join(e,_e);if(!w(t))throw new Error(`No ${_e} in ${e}. Only projects created with next-maker 5+ can be reconfigured; run \`next-maker init\` for a new project.`);return JSON.parse(await be(t,"utf-8"))},De=async(e,t={})=>{let r=e.starter.source.startsWith("/")&&w(e.starter.source)?e.starter.source:void 0,n=V({starterPath:t.starterPath??r}),o=await qi(A.join(Ji(),"next-maker-starter-"));await Tt(o,{source:n});let s=await Xe(o),i=await L(o);return{dir:o,source:n,manifest:s,files:i,dispose:()=>Cn(o,{recursive:!0,force:!0})}},Rn=(e,t)=>e[t]??{},zi=e=>[...e.off?.anchors??[],...e.on?.anchors??[]],Wi=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 be(A.join(e.dir,o),"utf-8")}catch{continue}if(!ze(s))continue;let i=s.split(`
17
+ `),a=[];for(let c=0;c<i.length;c++){let l=i[c].match(n);if(l)if(l[1]==="start"){let p=c+1;for(;p<i.length&&!/@next-maker:[\w-]+:end/.test(i[p]);)a.push(i[p++]);c=p}else l[1]==="end"||(/^\s*(\{\s*\/\*|\/\*|\/\/|#)/.test(i[c])?i[c+1]!==void 0&&a.push(i[++c]):a.push(i[c].replace(/\s*(\{\s*\/\*|\/\*|\/\/|#).*$/,"")))}a.length&&r.push({file:o,lines:a})}return r},On=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=Rn(r,"off"),o=Rn(r,"on"),s=new Set(e.manifest.always.remove.flatMap(c=>ie(e.files,c))),i=[...new Set((n.remove??[]).flatMap(c=>ie(e.files,c)))].filter(c=>!s.has(c)).sort(),a=o.overlay?(await L(A.join(e.dir,ye,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 Wi(e,zi(r))}},An=e=>/\.(test|spec)\.tsx?$/.test(e)||e.startsWith("test/")||e.includes("__test-utils__"),ir=async(e,t,r)=>{let n=JSON.parse(await be(A.join(e,"package.json"),"utf-8")),o=t.answers.tests!==!1,s=[],i=Object.values(r.manifest.features).filter(c=>!ae(r.manifest,c,t.answers)).flatMap(c=>c.off?.remove??[]),a=new Set(i.flatMap(c=>ie(r.files,c)));for(let[c,l]of Object.entries(r.manifest.features)){let p=ae(r.manifest,l,t.answers);if(!p){s.push({id:c,on:p,drift:[]});continue}let u=await On(r,c),f=[];for(let d of[...u.files,...u.overlayFiles])!o&&An(d)||a.has(d)||w(A.join(e,d))||f.push({kind:"missingFile",file:d});for(let d of u.packages)n.dependencies?.[d]||f.push({kind:"missingPackage",name:d,dev:!1});for(let d of u.devPackages)n.devDependencies?.[d]||f.push({kind:"missingPackage",name:d,dev:!0});for(let d of u.scripts)n.scripts?.[d]||f.push({kind:"missingScript",name:d});s.push({id:c,on:p,drift:f})}return s},In=async(e,t,r,n)=>{let o=t.answers,s=JSON.parse(await be(A.join(r.dir,"package.json"),"utf-8")),i=[],a=[];for(let[p,u]of Object.entries(r.manifest.features)){let f=ae(r.manifest,u,o),d=ae(r.manifest,u,n);f!==d&&(d?i:a).push(await On(r,p))}let c=n.tests!==!1,l={before:o,after:n,turningOn:i,turningOff:a,copy:[],remove:[],addPackages:{},addDevPackages:{},removePackages:[],addScripts:{},removeScripts:[],unwrapJsx:[],unwrapCall:[],manual:[]};for(let p of a){for(let u of[...p.files,...p.overlayFiles])w(A.join(e,u))&&l.remove.push(u);l.removePackages.push(...p.packages,...p.devPackages),l.removeScripts.push(...p.scripts),l.unwrapJsx.push(...p.feature.off?.unwrapJsx??[]),l.unwrapCall.push(...p.feature.off?.unwrapCall??[]),p.snippets.length&&l.manual.push({feature:p.id,direction:"delete",snippets:p.snippets})}for(let p of i){for(let u of[...p.files,...p.overlayFiles])!c&&An(u)||(!w(A.join(e,u))||p.overlayFiles.includes(u))&&l.copy.push(u);for(let u of p.packages)l.addPackages[u]=s.dependencies?.[u]??"latest";for(let u of p.devPackages)l.addDevPackages[u]=s.devDependencies?.[u]??"latest";for(let u of p.scripts)s.scripts?.[u]&&(l.addScripts[u]=s.scripts[u]);p.snippets.length&&l.manual.push({feature:p.id,direction:"add",snippets:p.snippets})}return l.copy=[...new Set(l.copy)].sort(),l.remove=[...new Set(l.remove)].sort(),l},Nn=async(e,t,r,n)=>{for(let i of r.remove)await Cn(A.join(e,i),{force:!0});for(let i of r.copy){let a=r.turningOn.find(p=>p.overlayFiles.includes(i)),c=a?.feature.on?.overlay?A.join(t.dir,ye,a.feature.on.overlay,i):A.join(t.dir,i),l=A.join(e,i);await Hi(A.dirname(l),{recursive:!0}),await Vi(c,l,{force:!0})}for(let{file:i,tag:a}of r.unwrapJsx){let c=A.join(e,i);if(!w(c))continue;let l=await be(c,"utf-8"),p=We(l,a);p!==l&&await Ot(c,p,"utf-8")}for(let{file:i,name:a}of r.unwrapCall){let c=A.join(e,i);if(!w(c))continue;let l=await be(c,"utf-8"),p=Ke(l,a);p!==l&&await Ot(c,p,"utf-8")}let o=A.join(e,"package.json"),s=JSON.parse(await be(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=jn(s.dependencies),s.devDependencies=jn(s.devDependencies),await Ot(o,`${JSON.stringify(s,null,2)}
18
+ `,"utf-8"),await ue(e,{...n,answers:r.after})},jn=e=>Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)));var Xi=(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),Fn=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(w(n))for(let o of await Ki(n))r.push(de.join(n,o));for(let o of r){if(!w(o))continue;let s=await j(o),i=Xi(s,t);i!==s&&await P(o,i)}},Mn=async(e,t)=>{let r=de.join(e,C.ENV_EXAMPLE);if(!w(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 P(r,n)},Yi=(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)}`},_n=async(e,t,r,n,o)=>{let s=de.join(e,C.README);if(!t.readme){await xn(s);return}let i=Object.entries(n).filter(([l])=>l!=="packageManager").map(([l,p])=>Yi(r,l,p)).filter(l=>l!==null),a=[["dev","development server"],["build","production build"],["start","serve the production build"],["lint:fix","lint, format, and sort imports"],["type-check","route types + tsc"],["check:deprecated","fail on deprecated API usage"],["validate","every gate CI runs"]];n.tests&&a.push(["test","unit and component tests"]),n.e2e&&a.push(["test:e2e","Playwright against a production build"]);let c=`# ${t.projectName}
115
19
 
116
- export function renderWithProviders(
117
- ui: ReactElement,
118
- { store, preloadedState, ...renderOptions }: ExtendedRenderOptions = {},
119
- ) {
120
- const testStore = store ?? makeStore(preloadedState);
121
-
122
- return {
123
- store: testStore,
124
- ...render(ui, {
125
- wrapper: ({ children }) => <TestProviders store={testStore}>{children}</TestProviders>,
126
- ...renderOptions,
127
- }),
128
- };
129
- }
130
-
131
- export * from '@testing-library/react';
132
- export { default as userEvent } from '@testing-library/user-event';
133
- `,rc=`import { type RenderOptions, render } from '@testing-library/react';
134
- import { NextIntlClientProvider } from 'next-intl';
135
- import type { ReactElement, ReactNode } from 'react';
136
- import type { SupportedLocale } from '@/types/i18n';
137
-
138
- type TestProvidersProps = {
139
- children: ReactNode;
140
- messages?: Record<string, unknown>;
141
- locale?: SupportedLocale;
142
- };
20
+ ${t.description}
143
21
 
144
- export function TestProviders({ children, messages = {}, locale = 'en' }: TestProvidersProps) {
145
- return (
146
- <NextIntlClientProvider locale={locale} messages={messages} timeZone="UTC">
147
- {children}
148
- </NextIntlClientProvider>
149
- );
150
- }
22
+ Generated from [@teispace/nextjs-starter](https://github.com/teispace/nextjs-starter) ${r.starter.version} with \`@teispace/next-maker\`.
151
23
 
152
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'> & {
153
- messages?: Record<string, unknown>;
154
- locale?: SupportedLocale;
155
- };
24
+ ## Getting started
156
25
 
157
- export function renderWithProviders(
158
- ui: ReactElement,
159
- { messages, locale, ...renderOptions }: ExtendedRenderOptions = {},
160
- ) {
161
- return render(ui, {
162
- wrapper: ({ children }) => (
163
- <TestProviders messages={messages} locale={locale}>
164
- {children}
165
- </TestProviders>
166
- ),
167
- ...renderOptions,
168
- });
169
- }
170
-
171
- export * from '@testing-library/react';
172
- export { default as userEvent } from '@testing-library/user-event';
173
- `,ic=`import { type RenderOptions, render } from '@testing-library/react';
174
- import type { ReactElement, ReactNode } from 'react';
26
+ \`\`\`bash
27
+ ${o} install
28
+ ${ce(o,"dev")}
29
+ \`\`\`
175
30
 
176
- type TestProvidersProps = {
177
- children: ReactNode;
178
- };
31
+ Open [http://localhost:3000](http://localhost:3000). Copy \`.env.example\` to \`.env\` and set \`NEXT_PUBLIC_API_URL\` to your API origin. Production builds also need \`NEXT_PUBLIC_APP_URL\`, the public URL of this app.
179
32
 
180
- export function TestProviders({ children }: TestProvidersProps) {
181
- return <>{children}</>;
182
- }
33
+ ## What is included
183
34
 
184
- type ExtendedRenderOptions = Omit<RenderOptions, 'wrapper'>;
35
+ ${i.map(l=>`- ${l}`).join(`
36
+ `)}
185
37
 
186
- export function renderWithProviders(ui: ReactElement, options: ExtendedRenderOptions = {}) {
187
- return render(ui, {
188
- wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
189
- ...options,
190
- });
191
- }
38
+ ## Scripts
192
39
 
193
- export * from '@testing-library/react';
194
- export { default as userEvent } from '@testing-library/user-event';
195
- `;import Yi 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 Ki=async(e,t)=>{if(t.bundleAnalyzer)return;let r=Yi.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 _(Yi.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 Zi=async(e,t)=>{t.darkMode||(await C(et.join(e,a.THEME_PROVIDER)),await nc(e),await oc(e),await ac(e))},nc=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)},oc=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)}},sc=e=>e.replace(/\bbg-light\b\s?/g,"").replace(/\bdark:bg-dark\b\s?/g,"").replace(/\s{2,}/g," ").replace(/suppressHydrationWarning=\{true\}/g,""),ac=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=sc(i);n!==i&&await h(r,n)}};P();w();Be();import j from"node:path";var on=async(e,t)=>{let r=j.join(e,a.HTTP_UTILS),i=t.httpClient!=="none"||!!t.reactSecureStorage;t.httpClient==="none"?(await rt(e),await fc(e,r,i)):t.httpClient==="axios"?(await gc(e,r),await we(e,["axios"]),await Pe(e,["axios"])):t.httpClient==="fetch"?(await hc(e,r),await we(e,["fetch"]),await Pe(e,["fetch"])):t.httpClient},fc=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';
196
- `),await C(j.join(e,a.CLIENT_UTILS)),await Ue(e,[])):(await v(t),await yc(e),await C(j.join(e,a.APP_APIS)),await xc(e)),await wc(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 Pc(e))},gc=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)}},hc=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)}},yc=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)}},xc=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)}},wc=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=Dr(r),await h(t,r)}},Pc=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 sn=async(e,t)=>{t.i18n||(await Sc(e),await Ec(e),await Tc(e),await Cc(e),await vc(e))},vc=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)},Sc=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))},Ec=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)}},Tc=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)}},Cc=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 an=async(e,t)=>{t.httpClient!=="none"||t.reactSecureStorage||await v(ve.join(e,a.STORAGE_SERVICE))},cn=async e=>{await C(ve.join(e,a.LICENSE))},pn=async e=>{await C(ve.join(e,a.CHANGELOG))},ln=async e=>{await C(ve.join(e,a.NVM_RC)),await C(ve.join(e,a.NPM_RC))},mn=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 dn from"node:path";P();w();var un=async(e,t)=>{if(t.reactCompiler)return;let r=dn.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 _(dn.join(e,a.PACKAGE_JSON),i=>(i.devDependencies&&delete i.devDependencies[R.REACT_COMPILER_BABEL],i))};P();w();import $e from"node:path";var fn=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 Rc(e),await bc(e))},Rc=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)},bc=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 gn from"node:path";var hn=async(e,t)=>{if(t.i18n)return;let r=gn.join(e,a.SEO_FILE);l(r)&&await h(r,Ic);let i=gn.join(e,a.SITEMAP_FILE);l(i)&&await h(i,$c)},Ic=`import type { Metadata } from 'next';
40
+ | Script | Purpose |
41
+ | :-- | :-- |
42
+ ${a.map(([l,p])=>`| \`${ce(o,l)}\` | ${p} |`).join(`
43
+ `)}
197
44
 
198
- import { env } from '@/lib/env';
45
+ ## Guides
199
46
 
200
- const APP_URL = env.NEXT_PUBLIC_APP_URL;
201
- const APP_NAME = 'Nextjs Starter';
202
- /** Route served by \`src/app/opengraph-image.tsx\`; relative so \`metadataBase\` resolves it. */
203
- 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
204
52
 
205
- type SEOParams = {
206
- title: string;
207
- description: string;
208
- path?: string;
209
- image?: string;
210
- noIndex?: boolean;
211
- };
53
+ Generators: \`npx @teispace/next-maker feature <name>\`, \`page <name>\`, \`component <name>\`, \`env <NAME>\`${n.i18n?", `locale <code>`":""}.
54
+ `;await P(s,c)};var ve=async e=>{await Tt(e.projectPath,{source:e.source});let t=await Xe(e.projectPath);return nt(t,e.supplied,e.packageManager)},nt=(e,t,r)=>{let{answers:n,forced:o,unknown:s}=Ye(e,t),i=n.packageManager??r??"pnpm";return{manifest:e,answers:n,forced:o,unknown:s,packageManager:i,plan:er(e,n,i)}},$e=async(e,t,r,n)=>{let{manifest:o,answers:s,packageManager:i,plan:a}=r,c=await rr(e,o,a);return await Tn(e,t),await Fn(e,t),await Mn(e,t),await _n(e,t,o,s,i),await ue(e,{cli:Ae.version,starter:{name:o.starter.name,version:o.starter.version,source:n.location},packageManager:i,answers:s,identity:{projectName:t.projectName,description:t.description,author:t.author,version:t.version,email:t.email,gitRemote:t.gitRemote,readme:t.readme}}),c};var ar=["pnpm","npm","yarn","bun"],Zi=e=>{if(e!==void 0){if(!ar.includes(e))throw new Error(`Invalid --package-manager "${e}". Valid: ${ar.join(", ")}.`);return e}},ea=(e,t=[])=>[...t,e],Dn=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 (${Fe().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",ea).option("--package-manager <pm>",`Package manager (${ar.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 oa(t,r)}catch(n){b(`${n instanceof Error?n.message:n}`),process.exit(1)}})},ot=async(e,t,r,n)=>{let o=St(e);n.current=o;try{let s=await r();return o.succeed(t),s}finally{n.current===o&&(n.current=null)}},ta=e=>Array.isArray(e)?e.length?e.join(", "):"none":String(e),ra=(e,t,r)=>{m(I.bold("Options"));for(let[n,o]of Object.entries(t.answers)){let s=e.options[n]?.label??n;m(` ${I.dim(s.padEnd(44))} ${ta(o)}`)}for(let n of r)m(I.yellow(` ! ${n.reason}; ${n.option} turned off`));m(""),m(I.bold("Composition")),m(` ${I.green("keep")} ${t.featuresOn.join(", ")||"(none)"}`),m(` ${I.red("drop")} ${t.featuresOff.join(", ")||"(none)"}`),(t.featureOverlays.length||t.pmOverlays.length)&&m(` ${I.cyan("overlay")} ${[...t.pmOverlays,...t.featureOverlays].join(", ")}`),(t.packages.length||t.devPackages.length)&&m(` ${I.red("uninstall")} ${[...t.packages,...t.devPackages].join(", ")}`),m("")},na=async(e,t)=>{let r=t.config?await nr(t.config):void 0,n=Zi(t.packageManager??r?.packageManager),s={...t.preset?Me(t.preset):r?.preset?Me(r.preset):{},...r?.options??{},...xe(t.set)};n&&(s.packageManager=n);let i=!(t.yes||r);return{identity:i?await En(e):me(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}},oa=async(e,t)=>{Pt();let r=await na(e,t),{identity:n}=r,o=At.resolve(process.cwd(),n.projectName);if(w(o))throw new Error(`Directory ${n.projectName} already exists.`);let s={current:null},i=async()=>{w(o)&&(s.current?.stop(),s.current=null,m(I.yellow(`
55
+ Cleaning up ${n.projectName}...`)),await tt(o))},a=$t(async()=>{m(I.red(`
56
+ Process interrupted.`)),await i()});try{let c=V({starterPath:t.starterPath}),l=await ot(`Fetching starter (${c.location})...`,"Starter fetched.",()=>ve({projectPath:o,source:c,supplied:r.suppliedOptions,packageManager:r.packageManager}),s);if(m(I.dim(` ${l.manifest.starter.name} ${l.manifest.starter.version}`)),m(""),r.interactive){let d={...r.suppliedOptions,...await Ct(l.manifest,l.answers)};t.packageManager&&(d.packageManager=t.packageManager),l=nt(l.manifest,d,r.packageManager),m("")}for(let d of l.unknown)m(I.yellow(` ! unknown option "${d}" ignored`));let{packageManager:p,plan:u,manifest:f}=l;if(ra(f,u,l.forced),t.dryRun){a(),await tt(o),m(I.cyan("Dry run: nothing was created."));return}await ot("Composing project...","Project composed.",()=>$e(o,n,l,c),s),r.install&&(await ot(`Installing dependencies with ${p}...`,"Dependencies installed.",()=>le(o,p),s),await ot("Formatting...","Formatted.",()=>jt(o,p,"lint:fix"),s)),n.copyEnv&&w(At.join(o,".env.example"))&&await Qi(At.join(o,".env.example"),At.join(o,".env")),r.git&&await ot("Initializing git...","Git initialized.",()=>Rt(o,n.gitRemote,n.pushToRemote),s),a(),m(""),m(I.green(`\u2728 ${n.projectName} is ready.`)),m(""),m("Next:"),m(I.cyan(` cd ${n.projectName}`)),r.install||m(I.cyan(` ${p} install`)),m(I.cyan(` ${p==="pnpm"||p==="yarn"?`${p} dev`:`${p} run dev`}`)),m("")}catch(c){throw a(),s.current?.fail("Failed to create project."),s.current=null,await i(),c}};import Fr from"node:path";import X from"picocolors";import It from"node:path";var Ln=/^[A-Za-z][A-Za-z0-9-]*$/,sa=/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;function Un(e){return sa.test(e)}function M(e,t="name"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);if(!Ln.test(r))throw new Error(`Invalid ${t} "${e}". Use letters, digits, and hyphens only \u2014 no slashes, spaces, "..", or absolute paths.`);if(Un(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(!Ln.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(Un(o))throw new Error(`Invalid ${t} "${e}". Segment "${o}" is reserved on Windows.`)}return n}function ne(e,...t){let r=It.resolve(e),n=It.resolve(r,...t),o=It.relative(r,n);if(o===""||o===".")return n;if(o.startsWith("..")||It.isAbsolute(o))throw new Error(`Refusing to write outside the project directory (resolved path escapes "${r}").`);return n}import{execFile as ia}from"node:child_process";import{stat as aa}from"node:fs/promises";import cr from"node:path";import{promisify as ca}from"node:util";var pa=ca(ia),la=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".jsonc",".css"]),ma=e=>cr.join(e,"node_modules",".bin","biome"),pr=async(e,t,r=e)=>{let n=ma(r);if(t.length===0||!w(n))return!1;try{await pa(n,["check","--write","--no-errors-on-unmatched",...t],{cwd:e,maxBuffer:64*1024*1024})}catch(o){if(o.code==="ENOENT")return!1}return!0},k=async(e,t)=>{let r=[];for(let n of await L(e))if(la.has(cr.extname(n)))try{(await aa(cr.join(e,n))).mtimeMs>=t-1e3&&r.push(n)}catch{}return r.length&&await pr(e,r),r};import{access as ua}from"node:fs/promises";import da from"node:path";var ke=async(e,t,r)=>{try{return await ua(da.join(e,r,t)),!0}catch{return!1}};import{access as fa,readFile as Bn}from"node:fs/promises";import lr from"node:path";var Le=async e=>{try{return await fa(e),!0}catch{return!1}},Gn=async e=>{try{return JSON.parse(await Bn(e,"utf-8"))}catch{return null}},O=async e=>{let t=await Gn(lr.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 Gn(lr.join(e,_e)),o=(...y)=>lr.join(e,...y),s=await Le(o("src","store")),i=!!(r["@reduxjs/toolkit"]&&r["react-redux"])&&s,a=!!r.zustand&&s&&!i,c=i?"redux":a?"zustand":"none",l=await Le(o("src","lib","http","core","client.ts")),p=!!r.axios&&await Le(o("src","lib","http","adapters","axios.ts")),u="none";if(l)if(!p)u="fetch";else{let y="";try{y=await Bn(o("src","lib","http","client.ts"),"utf-8")}catch{}u=y.includes("axiosAdapter")?"axios":"both"}let f=!!r["next-intl"]&&await Le(o("src","i18n")),d=!!r.vitest||await Le(o("vitest.config.ts")),g=r["socket.io-client"]&&await Le(o("src","lib","ws"))?"present":"none";return{state:c,hasRedux:i,httpClient:u,hasI18n:f,hasTests:d,ws:g,hasQuery:!!r["@tanstack/react-query"],hasActions:!!r["next-safe-action"],record:n??void 0}};import{mkdir as ga,writeFile as ha}from"node:fs/promises";import mr from"node:path";var Vn=({pascalName:e,camelName:t})=>`import { z } from 'zod';
212
57
 
213
- export function generateSEOMetadata({
214
- title,
215
- description,
216
- path = '',
217
- image,
218
- noIndex = false,
219
- }: SEOParams): Metadata {
220
- const url = \`\${APP_URL}\${path}\`;
221
- const ogImage = image ?? DEFAULT_OG_IMAGE_PATH;
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>;
222
69
 
223
- return {
224
- title,
225
- description,
226
- metadataBase: new URL(APP_URL),
227
- alternates: {
228
- canonical: url,
229
- },
230
- openGraph: {
231
- type: 'website',
232
- title,
233
- description,
234
- url,
235
- siteName: APP_NAME,
236
- images: [
237
- {
238
- url: ogImage,
239
- width: 1200,
240
- height: 630,
241
- alt: title,
242
- },
243
- ],
244
- locale: 'en_US',
245
- },
246
- twitter: {
247
- card: 'summary_large_image',
248
- title,
249
- description,
250
- images: [ogImage],
251
- },
252
- robots: noIndex
253
- ? { index: false, follow: false }
254
- : {
255
- index: true,
256
- follow: true,
257
- 'max-video-preview': -1,
258
- 'max-image-preview': 'large' as const,
259
- 'max-snippet': -1,
260
- },
261
- };
262
- }
70
+ export const ${t}ListSchema = z.array(${t}Schema);
263
71
 
264
- export { APP_NAME, APP_URL, DEFAULT_OG_IMAGE_PATH };
265
- `,$c=`import type { MetadataRoute } from 'next';
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>;
266
76
 
267
- import { env } from '@/lib/env';
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
+ `,Hn=({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
+ `,qn=({pascalName:e,camelName:t})=>`import 'server-only';
268
91
 
269
- const APP_URL = env.NEXT_PUBLIC_APP_URL;
92
+ import { AppApis } from '@/lib/config/app-apis';
93
+ import { serverHttp } from '@/lib/http/server';
270
94
 
271
- export default function sitemap(): MetadataRoute.Sitemap {
272
- const paths = ['/'];
95
+ import { ${t}ListSchema, ${t}Schema } from './schema';
273
96
 
274
- return paths.map((path) => ({
275
- url: \`\${APP_URL}\${path}\`,
276
- lastModified: new Date(),
277
- changeFrequency: 'monthly' as const,
278
- priority: path === '/' ? 1 : 0.8,
279
- }));
280
- }
281
- `;import kc from"node:fs/promises";import it from"node:path";P();w();var Nc=[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],yn=async e=>{let t;try{t=await kc.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 yn(i)}else r.isFile()&&/\.(test|spec)\.tsx?$/.test(r.name)&&await C(i)}},xn=async(e,t)=>{t.tests||(await C(it.join(e,a.VITEST_CONFIG)),await v(it.join(e,a.TEST_DIR)),await yn(it.join(e,a.SRC)),await _(it.join(e,a.PACKAGE_JSON),r=>{if(r.devDependencies)for(let i of Nc)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 wn from"node:path";import{readFile as Ac,writeFile as _c}from"node:fs/promises";import Fc 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+`
282
- `+e.slice(o+n.length)}return`${t}
283
- ${e}`},Gr=(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=`
284
- ${t}: ${r},`,c=o.trimEnd()+s;return e.replace(i,`combineReducers({${c}
285
- })`)},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()}
286
- ${t}
287
- ${n}`};var ke=async e=>{let{projectPath:t,name:r,persist:i,importPath:n}=e,o=Fc.join(t,"src","store","rootReducer.ts");try{let s=await Ac(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=Gr(s,c,x),await _c(o,s)}catch(s){throw new Error(`Failed to register reducer '${r}' in rootReducer: ${s}`,{cause:s})}};var Pn="import { attachWsBridge, wsClient } from '@/lib/utils/ws';",Oc=`
288
- // Bridge the WS client's lifecycle into the Redux slice exactly once per
289
- // store instance. The bridge does not open a connection \u2014 that happens
290
- // lazily on first \`useWsEvent\` subscription or an explicit \`connect()\`.
291
- // Effect runs in the browser only, so the SSR boundary is safe.
292
- useEffect(() => {
293
- const store = storeRef.current;
294
- if (!store) return;
295
- return attachWsBridge(wsClient, store.dispatch);
296
- }, []);
297
- `,Ft=async e=>{await ke({projectPath:e,name:"ws",persist:!1,importPath:"src/store/slices/ws.slice"})},Dc=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)}${Pn}
298
- ${t.slice(n)}`,t=Gc(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
299
- ${Oc.trimEnd()}
300
- `),t},Ot=e=>{let t=e;return t=t.replace(new RegExp(`${Hc(Pn)}\\n?`),""),t=t.replace(/\n(?:[ \t]*\/\/[^\n]*\n)*[ \t]*useEffect\(\(\)\s*=>\s*\{[\s\S]*?attachWsBridge\(wsClient,\s*store\.dispatch\)[\s\S]*?\},\s*\[\]\);\n?/,`
301
- `),/\buseEffect\s*\(/.test(t)||(t=jc(t)),t},jc=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';
302
- `;return e.replace(t,n)},Dt=async e=>{let t=wn.join(e,a.STORE_PROVIDER);if(!l(t))return;let r=await g(t),i=Dc(r);i!==r&&await h(t,i)};var jt=async e=>{let t=wn.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+=Lc,i=!0),r.includes("vi.mock('server-only'")||(r+=Mc,i=!0),i&&await h(t,r)},Lc=`
303
- // \`react-secure-storage\` constructs an \`EncryptionService\` at module load
304
- // that fingerprints the browser via \`<canvas>.getContext\`. jsdom doesn't
305
- // implement canvas, so the import explodes anywhere it's pulled in
306
- // transitively (HTTP token store, WS auth carrier, etc.). Stub it globally \u2014
307
- // tests that need real behaviour are integration tests, not unit tests.
308
- vi.mock('react-secure-storage', () => {
309
- const store = new Map<string, unknown>();
310
- return {
311
- default: {
312
- setItem: (k: string, v: unknown) => store.set(k, v),
313
- getItem: (k: string) => store.get(k) ?? null,
314
- removeItem: (k: string) => store.delete(k),
315
- clear: () => store.clear(),
316
- },
317
- };
318
- });
319
- `,Mc=`
320
- // \`server-only\` throws at module load outside a Next.js server bundle \u2014
321
- // the package relies on bundler-level redirect to a no-op file in server
322
- // environments. Vitest doesn't do that redirect, so we stub it as a no-op
323
- // to let unit tests reach the server-side modules. Production builds are
324
- // unaffected; Next.js still routes \`server-only\` to its real implementation.
325
- vi.mock('server-only', () => ({}));
326
- `,Gc=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)},Hc=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 vn=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 Uc(e),await Bc(e)),await zc(e))},Uc=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)},Bc=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)},zc=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 Sn=async(e,t)=>{let r=S("Customizing features...");try{await on(e,t),await an(e,t),await vn(e,t),await fn(e,t),await Zi(e,t),await Ki(e,t),await sn(e,t),await hn(e,t),await un(e,t),await xn(e,t),await cn(e),await pn(e),await ln(e),await mn(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 Vc from"node:path";P();w();var En=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 _(Vc.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 Tn}from"node:fs/promises";import L from"node:path";P();w();var Cn=async(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),await Zc(e,t)},Jc=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)))},Wc=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)))},qc=async(e,t)=>{t.ci||await v(L.join(e,a.GITHUB_WORKFLOWS))},Xc=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 Tn(i);for(let c of s){let p=L.join(i,c),u=await g(p);u=o(u),await h(p,u)}}}catch{}}},Yc=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))},Kc=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}
327
- `};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)}}},Zc=async(e,t)=>{let r=async o=>{let s=await Tn(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}
97
+ export const ${t.toUpperCase()}_TAG = '${t}';
328
98
 
329
- ${t.description}
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 });
330
109
 
331
- ## Getting Started
110
+ export const get${e} = async (id: string) =>
111
+ serverHttp.get(AppApis.${t}.detail(id), { schema: ${t}Schema });
112
+ `,Jn=({pascalName:e,camelName:t})=>`'use server';
332
113
 
333
- First, run the development server:
114
+ import { revalidateTag } from 'next/cache';
334
115
 
335
- \`\`\`bash
336
- ${t.packageManager==="npm"?"npm run dev":`${t.packageManager} dev`}
337
- \`\`\`
116
+ import { authActionClient } from '@/lib/actions';
117
+ import { AppApis } from '@/lib/config/app-apis';
118
+ import { serverHttp } from '@/lib/http/server';
338
119
 
339
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
340
- `;await h(n,o)}else await C(n)};P();w();import Hr from"node:path";var Rn=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}>
341
- ${o}
342
- </NextIntlClientProvider>`),t.darkMode&&(o=`<CustomThemeProvider>
343
- ${o}
344
- </CustomThemeProvider>`),t.redux&&(o=`<StoreProvider preloadedState={preloadedState}>
345
- ${o}
346
- </StoreProvider>`),o==="{children}"&&(o="<>{children}</>");let s=`type RootProviderProps = {
347
- ${i.join(`
348
- `)}
349
- };`,c=`'use client';
350
- ${r.join(`
351
- `)}
120
+ import { create${e}InputSchema, ${t}Schema, update${e}InputSchema } from './schema';
121
+ import { ${t.toUpperCase()}_TAG } from './server';
352
122
 
353
- ${s}
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
+ });
354
139
 
355
- export const RootProvider = ({ ${n.join(", ")} }: RootProviderProps) => {
356
- return (
357
- ${o}
358
- );
359
- };
360
- `;await h(Hr.join(e,a.ROOT_PROVIDER),c)},bn=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.
361
- import { HttpClientBundleSentinel } from '@/lib/utils/http/__bundle-sentinel__/client-bundle-sentinel';
362
- `:"",s=n?`
363
- <HttpClientBundleSentinel />`:"",c=`import type { Metadata } from 'next';
364
- import '@/styles/globals.css';
365
- import { Livvic } from 'next/font/google';
366
- ${o}import { RootProvider } from '@/providers';
367
-
368
- const livvic = Livvic({
369
- subsets: ['latin'],
370
- variable: '--font-livvic',
371
- weight: ['100', '200', '300', '400', '500', '600', '700', '900'],
372
- display: 'swap',
373
- });
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
+ });
374
151
 
375
- export const metadata: Metadata = {
376
- title: '${t.projectName}',
377
- description: '${t.description}',
378
- };
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
+ `,zn=({pascalName:e,camelName:t})=>`import { queryOptions, useSuspenseQuery } from '@tanstack/react-query';
379
162
 
380
- export default function RootLayout({
381
- children,
382
- }: Readonly<{
383
- children: React.ReactNode;
384
- }>) {
385
- return (
386
- <html lang="en"${t.darkMode?" suppressHydrationWarning={true}":""}>
387
- <body className={${r}}>
388
- <RootProvider${i}>${s}
389
- {children}
390
- </RootProvider>
391
- </body>
392
- </html>
393
- );
394
- }
395
- `;await h(Hr.join(e,a.ROOT_LAYOUT),c);let p=t.redux?`import { Counter } from '@/features/counter';
163
+ import { AppApis } from '@/lib/config/app-apis';
164
+ import { http } from '@/lib/http';
165
+ import { unwrapForQuery } from '@/lib/query';
396
166
 
397
- `:"",u=t.redux?`
398
- <Counter />`:"",d=`${p}export default function Home() {
399
- return (
400
- <div className="flex min-h-screen flex-col items-center justify-center p-24">
401
- <h1 className="text-4xl font-bold">Welcome to ${t.projectName}</h1>
402
- <p className="mt-4 text-xl">Get started by editing src/app/page.tsx</p>${u}
403
- </div>
404
- );
405
- }
406
- `;await h(Hr.join(e,a.ROOT_PAGE),d)};import Qc from"degit";var In="teispace/nextjs-starter",$n="v1.1.1",ep=()=>$n==="main"?In:`${In}#${$n}`,J=async(e,t={})=>{await Qc(ep(),{cache:!1,force:!0,verbose:t.verbose??!1}).clone(e)};var kn=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 Br=["npm","yarn","pnpm","bun"],tp=e=>{if(e!==void 0){if(!Br.includes(e))throw new Error(`Invalid --package-manager "${e}". Valid: ${Br.join(", ")}.`);return e}},Nn=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: ${Br.join(", ")})`).action(async(t,r)=>{try{let i=tp(r.packageManager);await ip(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)}},rp=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("")},ip=async(e,t)=>{Hi();let r;t.yes?(r=Wi(e,{packageManager:t.packageManager}),m(ee.cyan(`\u2728 Bootstrapping ${r.projectName} with the recommended defaults`)),m(""),rp(r)):(m("Welcome to the Teispace Next.js App Creator!"),m(""),r=await qi(e),t.packageManager&&(r={...r,packageManager:t.packageManager}));let i=Ur.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(`
407
- 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=Li(async()=>{console.log(ee.red(`
408
- Process interrupted. Cleaning up...`)),await o()});try{if(await kn(i),await En(i,r),await Sn(i,r),await st("Generating providers and layout...","Providers and layout generated.",async()=>{await Rn(i,r),await bn(i,r)},n),await st("Setting up developer tools...","Developer tools configured.",()=>Cn(i,r),n),await st("Initializing Git...","Git initialized.",()=>Bi(i,r.gitRemote,r.pushToRemote),n),await st("Installing dependencies...","Dependencies installed.",()=>zi(i,r.packageManager),n),await st("Formatting and linting...","Formatted.",()=>K(i,r.packageManager,"lint:fix"),n),r.copyEnv){let c=Ur.join(i,".env.example"),p=Ur.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 ii from"node:path";import le from"picocolors";import Mt from"node:path";var An=/^[A-Za-z][A-Za-z0-9-]*$/,np=/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;function _n(e){return np.test(e)}function W(e,t="name"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);if(!An.test(r))throw new Error(`Invalid ${t} "${e}". Use letters, digits, and hyphens only \u2014 no slashes, spaces, "..", or absolute paths.`);if(_n(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(!An.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(_n(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 Ln,writeFile as Vr}from"node:fs/promises";import Ne from"node:path";w();var zr=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(`
409
- `)},jn=e=>`export { ${e.componentName} } from './${e.componentName}';
410
- `;var Jr=async e=>{let{name:t,projectPath:r,isClient:i,hasI18n:n,featurePath:o}=e,s=N(t);o?await cp(s,r,o,i,n):await pp(s,t,r,i,n)},cp=async(e,t,r,i,n)=>{let o=Ne.join(t,r,"components");await Ln(o,{recursive:!0}),await Vr(Ne.join(o,`${e}.tsx`),zr({componentName:e,isClient:i,hasI18n:n}))},pp=async(e,t,r,i,n)=>{let o=Ne.join(r,"src","components","common",e);await Ln(o,{recursive:!0}),await Vr(Ne.join(o,`${e}.tsx`),zr({componentName:e,isClient:i,hasI18n:n})),await Vr(Ne.join(o,"index.ts"),jn({componentName:e}));let s=Ne.join(r,"src","components","common","index.ts");await lp(s,`export { ${e} } from './${e}';
411
- `);let c=Ne.join(r,"src","components","index.ts");await mp(c)},lp=async(e,t)=>{let r="";l(e)&&(r=await g(e)),r.includes(t.trim())||(r+=t,await h(e,r))},mp=async e=>{let t="";l(e)&&(t=await g(e));let r=`export * from './common';
412
- `;t.includes(r.trim())||(t+=r,await h(e,t))};import{writeFile as dp}from"node:fs/promises";import up from"node:path";var Mn=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';
413
- ${n}
414
- import type { ResultAsync } from '@/types';
167
+ import { ${t}Keys } from './keys';
168
+ import { ${t}ListSchema, ${t}Schema } from './schema';
415
169
 
416
170
  /**
417
- * Summary type used in list responses (e.g., cards, tables).
171
+ * Client-side reads. A Server Component can \`prefetchQuery(${t}ListQuery())\`
172
+ * inside \`<HydrateQueries>\` so the hook below renders with data on first paint.
418
173
  */
419
- export interface ${r}Summary {
420
- id: string;
421
- // Add list/card fields here (title, thumbnail, status, etc.)
422
- }
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
+ });
423
180
 
424
- /**
425
- * Detail type used in single-item responses (e.g., detail page).
426
- */
427
- export interface ${r}Detail extends ${r}Summary {
428
- // Add detailed fields here (content, metadata, relations, etc.)
429
- }
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
+ });
430
187
 
431
- export interface Create${r}Dto {
432
- // Add create fields here
433
- }
188
+ export const use${e}List = () => useSuspenseQuery(${t}ListQuery());
189
+ export const use${e} = (id: string) => useSuspenseQuery(${t}Query(id));
190
+ `,Wn=({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 st=async e=>{let{name:t,featurePath:r,withActions:n}=e,o={kebabName:t,camelName:G(t),pascalName:$(t)},s=mr.join(r,"api");await ga(s,{recursive:!0});let i=[["schema.ts",Vn(o)],["keys.ts",Hn(o)],["server.ts",qn(o)],["queries.ts",zn(o)]];return n&&i.push(["actions.ts",Jn(o)]),await Promise.all(i.map(([a,c])=>ha(mr.join(s,a),c))),i.map(([a])=>mr.join("api",a))};import{mkdir as Xn,writeFile as dr}from"node:fs/promises";import Pe from"node:path";var ur=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
+ `)},Kn=e=>`export { ${e.componentName} } from './${e.componentName}';
198
+ `;var fr=async e=>{let{name:t,projectPath:r,isClient:n,hasI18n:o,featurePath:s}=e,i=$(t);s?await ya(i,r,s,n,o):await wa(i,t,r,n,o)},ya=async(e,t,r,n,o)=>{let s=Pe.join(t,r,"components");await Xn(s,{recursive:!0}),await dr(Pe.join(s,`${e}.tsx`),ur({componentName:e,isClient:n,hasI18n:o}))},wa=async(e,t,r,n,o)=>{let s=Pe.join(r,"src","components","common",e);await Xn(s,{recursive:!0}),await dr(Pe.join(s,`${e}.tsx`),ur({componentName:e,isClient:n,hasI18n:o})),await dr(Pe.join(s,"index.ts"),Kn({componentName:e}));let i=Pe.join(r,"src","components","common","index.ts");await xa(i,`export { ${e} } from './${e}';
199
+ `);let a=Pe.join(r,"src","components","index.ts");await ba(a)},xa=async(e,t)=>{let r="";w(e)&&(r=await j(e)),r.includes(t.trim())||(r+=t,await P(e,r))},ba=async e=>{let t="";w(e)&&(t=await j(e));let r=`export * from './common';
200
+ `;t.includes(r.trim())||(t+=r,await P(e,t))};import{mkdir as yr,writeFile as Ue}from"node:fs/promises";import K from"node:path";import{mkdir as va,writeFile as Se}from"node:fs/promises";import Ee from"node:path";var hr=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
+ `},Yn=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 { ${gr(t)}Keys } from './api/keys';`),a.push(`export { ${gr(t)}ListQuery, ${gr(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';
434
207
 
435
- export interface Update${r}Dto {
436
- // Add update fields here
437
- }
208
+ export { get${e.pascalName}, list${e.pascalName}s, ${e.pascalName.toUpperCase()}_TAG } from './api/server';
209
+ `,gr=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase());var Zn=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';
438
210
 
439
- export const ${t}Service = {
440
- getAll: (): ResultAsync<${r}Summary[]> => {
441
- return ${o}.get<${r}Summary[]>(AppApis.${t}.getAll);
442
- },
211
+ import type { ${t}State } from '${n}';
443
212
 
444
- getById: (id: string): ResultAsync<${r}Detail> => {
445
- return ${o}.get<${r}Detail>(AppApis.${t}.getById(id));
446
- },
213
+ const ${o} = 1;
447
214
 
448
- create: (data: Create${r}Dto): ResultAsync<${r}Detail> => {
449
- return ${o}.post<${r}Detail>(AppApis.${t}.create, data);
450
- },
451
-
452
- update: (id: string, data: Update${r}Dto): ResultAsync<${r}Detail> => {
453
- return ${o}.patch<${r}Detail>(AppApis.${t}.update(id), data);
454
- },
455
-
456
- delete: (id: string): ResultAsync<void> => {
457
- return ${o}.delete<void>(AppApis.${t}.delete(id));
458
- },
459
- };
460
- `},Gn=e=>{let{camelName:t,kebabName:r}=e;return` ${t}: {
461
- base: '/${r}',
462
- getAll: '/${r}',
463
- getById: (id: string) => \`/${r}/\${id}\`,
464
- create: '/${r}',
465
- update: (id: string) => \`/${r}/\${id}\`,
466
- delete: (id: string) => \`/${r}/\${id}\`,
467
- },`};var Wr=async e=>{let{name:t,outputPath:r,httpClient:i}=e,n=G(t),o=N(t);await dp(up.join(r,`${t}.service.ts`),Mn({camelName:n,pascalName:o,httpClient:i}))};import{mkdir as yp,writeFile as zt}from"node:fs/promises";import ie from"node:path";import{writeFile as fp}from"node:fs/promises";import gp from"node:path";var Hn=e=>{let{camelName:t,httpClient:r}=e;return`import { AppApis } from '@/lib/config';
468
- ${r==="axios"?"import { axiosClient } from '@/lib/utils/http';":"import { fetchClient } from '@/lib/utils/http';"}
469
- import type { ResultAsync } from '@/types';
470
-
471
- export const ${t}Service = {
472
- getAll: (): ResultAsync<string> => {
473
- return ${r==="axios"?"axiosClient":"fetchClient"}.get<string>(AppApis.${t}.getAll);
474
- },
475
- };
476
- `};var pt=async e=>{let{name:t,outputPath:r,httpClient:i}=e,n=G(t);await fp(gp.join(r,`${t}.service.ts`),Hn({camelName:n,httpClient:i}))};import{mkdir as hp,writeFile as lt}from"node:fs/promises";import mt from"node:path";var Un=e=>{let{sliceName:t,withPersist:r}=e;return`export * from './${t}.slice';
477
- export * from './${t}.selectors';
478
- export * from './${t}.types';${r?`
479
- export * from './persist';`:""}
480
- `},Bn=e=>{let{featureName:t,componentName:r,hookName:i,withStore:n,withService:o}=e;return`export { default as ${r} } from './components/${r}';
481
- export { ${i} } from './hooks/${i}';
482
- export * from './types/${t}.types';${n?`
483
- export * from './store';`:""}${o?`
484
- export * from './services/${t}.service';`:""}
485
- `};var zn=e=>{let{componentName:t,camelName:r,typesImportPath:i}=e,n=`${r.toUpperCase()}_PERSIST_VERSION`;return`import { createMigrate, type PersistConfig, type PersistedState } from 'redux-persist';
486
- import storage from '@/store/storage';
487
- import type { ${t}State } from '${i}';
488
-
489
- const ${n} = 1;
490
-
491
- const migrations = {
492
- // Bump ${n} and add a migration here when ${t}State's
493
- // shape changes. Receives the previously-persisted state, returns the new
494
- // shape. Returning \`undefined\` discards stale state.
495
- // 2: (state) => ({ ...state, newField: defaultValue }),
496
- } satisfies Record<number, (state: PersistedState) => PersistedState>;
497
-
498
- export const ${r}PersistConfig: PersistConfig<${t}State> = {
215
+ export const ${r}Persistence = definePersistence<${t}State>({
499
216
  key: '${r}',
500
- storage,
501
- version: ${n},
502
- // whitelist: ['someField'], // Specify which fields to persist
503
- migrate: createMigrate(migrations, { debug: false }),
504
- };
505
- `};var Vn=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 eo=e=>{let{componentName:t,camelName:r}=e;return`import type { RootState } from '@/store/rootReducer';
506
228
 
507
229
  export const select${t}State = (state: RootState) => state.${r};
508
- export { setLoading, setError, resetState } from './${i}.slice';
509
- `};var Jn=e=>{let{componentName:t,camelName:r,typesImportPath:i}=e;return`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
510
- 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 to=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}';
511
235
 
512
236
  const initialState: ${t}State = {
513
- loading: false,
237
+ status: 'idle',
514
238
  error: null,
515
- // Add your initial state here
516
239
  };
517
240
 
518
241
  export const ${r}Slice = createSlice({
519
242
  name: '${r}',
520
243
  initialState,
521
244
  reducers: {
522
- setLoading: (state, action: PayloadAction<boolean>) => {
523
- state.loading = action.payload;
245
+ started: (state) => {
246
+ state.status = 'loading';
247
+ state.error = null;
524
248
  },
525
- setError: (state, action: PayloadAction<string | null>) => {
249
+ failed: (state, action: PayloadAction<string>) => {
250
+ state.status = 'error';
526
251
  state.error = action.payload;
527
252
  },
528
- resetState: (state) => {
529
- state.loading = false;
530
- state.error = null;
531
- },
253
+ reset: () => initialState,
532
254
  },
533
255
  });
534
256
 
535
- export const { setLoading, setError, resetState } = ${r}Slice.actions;
536
-
257
+ export const { started, failed, reset } = ${r}Slice.actions;
537
258
  export const ${r}Reducer = ${r}Slice.reducer;
538
- `};var Bt=e=>{let{componentName:t,withStore:r}=e;return`export interface ${t}State {
539
- ${r?`loading: boolean;
259
+ `};var Nt=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;
540
263
  error: string | null;
541
- // Add your state properties here`:`// Add your state properties here
542
- // example: value: string;`}
543
264
  }
544
- `};var dt=async e=>{let{name:t,outputPath:r,persist:i,typesImportPath:n=`./${t}.types`}=e;await hp(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`),Jn({componentName:o,camelName:s,typesImportPath:n})),lt(mt.join(r,`${t}.selectors.ts`),Vn({componentName:o,camelName:s,sliceName:t})),...i?[lt(mt.join(r,"persist.ts"),zn({componentName:o,camelName:s,typesImportPath:n}))]:[],lt(mt.join(r,"index.ts"),Un({sliceName:t,withPersist:i}))])};var Wn=e=>{let{componentName:t,hookName:r}=e;return`'use client';
545
- import { ${r} } from '../hooks/${r}';
265
+ `:`export interface ${t}Props {
266
+ // Props for the ${t} component
267
+ }
268
+ `};var ro=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
+ }
278
+
279
+ const initial: ${t}State = { status: 'idle', error: null };
546
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 it=async e=>{let{name:t,outputPath:r,persist:n,state:o="redux",typesImportPath:s=`./${t}.types`}=e;await va(r,{recursive:!0});let i=$(t),a=G(t),c=[];s===`./${t}.types`&&c.push(Se(Ee.join(r,`${t}.types.ts`),Nt({componentName:i,withStore:!0}))),o==="zustand"?c.push(Se(Ee.join(r,`${t}.store.ts`),ro({componentName:i,camelName:a,typesImportPath:s})),Se(Ee.join(r,"index.ts"),hr({sliceName:t,withPersist:!1,state:o}))):(c.push(Se(Ee.join(r,`${t}.slice.ts`),to({componentName:i,camelName:a,typesImportPath:s})),Se(Ee.join(r,`${t}.selectors.ts`),eo({componentName:i,camelName:a,sliceName:t})),Se(Ee.join(r,"index.ts"),hr({sliceName:t,withPersist:n,state:o}))),n&&c.push(Se(Ee.join(r,"persist.ts"),Zn({componentName:i,camelName:a,typesImportPath:s})))),await Promise.all(c)};var no=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
+ `:"",c=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 \`${$a(o)}ListQuery()\` in the
304
+ * page and wrap this component in \`<HydrateQueries>\` for a no-spinner first paint.
305
+ */
547
306
  export function ${t}() {
548
- const {} = ${r}();
307
+ ${a} const { data } = use${o}List();
549
308
 
550
309
  return (
551
- <div>
552
- <h2>${t} Component</h2>
553
- {/* Add your component UI here */}
554
- </div>
310
+ <section className="flex flex-col gap-2">
311
+ <h2 className="font-medium text-sm">${c}</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>
555
318
  );
556
319
  }
320
+ `:`'use client';
557
321
 
558
- export default ${t};
559
- `};var qn=e=>{let{hookName:t,componentName:r,featureName:i}=e;return`'use client';
560
- import { useAppDispatch, useAppSelector } from '@/store/hooks';
561
- import { select${r}State, setLoading, setError } from '../store/${i}.selectors';
322
+ ${i}import { ${r} } from '../hooks/${r}';
562
323
 
563
- export const ${t} = () => {
564
- const dispatch = useAppDispatch();
565
- const state = useAppSelector(select${r}State);
324
+ export function ${t}() {
325
+ ${a} const state = ${r}();
326
+
327
+ return (
328
+ <section className="flex flex-col gap-2">
329
+ <h2 className="font-medium text-sm">${c}</h2>
330
+ <pre className="text-xs">{JSON.stringify(state, null, 2)}</pre>
331
+ </section>
332
+ );
333
+ }
334
+ `},$a=e=>e.charAt(0).toLowerCase()+e.slice(1);var oo=e=>{let{hookName:t,componentName:r,featureName:n,state:o}=e;return o==="zustand"?`'use client';
566
335
 
567
- const handleSetLoading = (loading: boolean) => {
568
- dispatch(setLoading(loading));
569
- };
336
+ import { useAppStore } from '@/store/hooks';
570
337
 
571
- const handleSetError = (error: string | null) => {
572
- dispatch(setError(error));
573
- };
338
+ export const ${t} = () => {
339
+ const status = useAppStore((state) => state.${at(n)}.status);
340
+ const error = useAppStore((state) => state.${at(n)}.error);
341
+ const start = useAppStore((state) => state.${at(n)}Started);
342
+ const fail = useAppStore((state) => state.${at(n)}Failed);
343
+ const reset = useAppStore((state) => state.${at(n)}Reset);
574
344
 
575
- return {
576
- state,
577
- setLoading: handleSetLoading,
578
- setError: handleSetError,
579
- } as const;
345
+ return { status, error, start, fail, reset } as const;
580
346
  };
581
- `},Xn=e=>{let{hookName:t}=e;return`'use client';
582
- 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';
583
353
 
584
354
  export const ${t} = () => {
585
- // Add your state and logic here
586
- const [state, setState] = useState({});
355
+ const dispatch = useAppDispatch();
356
+ const status = useAppSelector(select${r}Status);
357
+ const error = useAppSelector(select${r}Error);
587
358
 
588
359
  return {
589
- state,
590
- // Add your methods here
360
+ status,
361
+ error,
362
+ start: () => dispatch(started()),
363
+ fail: (message: string) => dispatch(failed(message)),
364
+ reset: () => dispatch(reset()),
591
365
  } as const;
592
366
  };
593
- `};var qr=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=>yp(d,{recursive:!0}))),await Promise.all([zt(ie.join(r,"components",`${c}.tsx`),Wn({componentName:c,hookName:p})),zt(ie.join(r,"hooks",`${p}.ts`),i?qn({hookName:p,componentName:c,featureName:t}):Xn({hookName:p})),zt(ie.join(r,"types",`${t}.types.ts`),Bt({componentName:c,withStore:i})),zt(ie.join(r,"index.ts"),Bn({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 xp,writeFile as wp}from"node:fs/promises";import Xr from"node:path";var Yn=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(`
594
- `)};var Yr=async e=>{let{name:t,projectPath:r,isClient:i,featurePath:n}=e,s=`use${N(t)}`,c;n?c=Xr.join(r,n,"hooks"):c=Xr.join(r,"src","hooks"),await xp(c,{recursive:!0}),await wp(Xr.join(c,`${s}.ts`),Yn({hookName:s,isClient:i}))};import{mkdir as Sp}from"node:fs/promises";import Vt from"node:path";w();var Pp=e=>`import { setRequestLocale } from 'next-intl/server';
595
- import type { SupportedLocale } from '@/types/i18n';
367
+ `},at=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),Ft=e=>{let{hookName:t}=e;return`'use client';
596
368
 
597
- type Props = {
598
- children: React.ReactNode;
599
- params: Promise<{ locale: string }>;
600
- };
369
+ import { useCallback, useState } from 'react';
601
370
 
602
- export default async function ${e}({ children, params }: Props) {
603
- const { locale } = await params;
604
- 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
+ }, []);
605
382
 
606
- return <>{children}</>;
607
- }
608
- `,vp=e=>`type Props = {
609
- children: React.ReactNode;
383
+ return { busy, run } as const;
610
384
  };
385
+ `};var Mt=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';
611
386
 
612
- export default function ${e}({ children }: Props) {
613
- return <>{children}</>;
614
- }
615
- `,Kn=({componentName:e,hasI18n:t})=>t?Pp(e):vp(e);var Ep=/^[a-z][a-z0-9-]*$/,Tp=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,Cp=e=>{if(!Ep.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.`)},Rp=e=>{if(!Tp.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},Kr=async e=>{let{segment:t,at:r,projectPath:i,hasI18n:n,withGroup:o}=e;Cp(t),r&&Rp(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 Sp(u,{recursive:!0}),await h(d,Kn({componentName:s,hasI18n:n})),{layoutFile:d,displayPath:Vt.relative(i,d),componentName:s}};w();import{copyFile as bp,readFile as Ip,writeFile as $p}from"node:fs/promises";import ut from"node:path";var Zr=async e=>{let{code:t,name:r,country:i,flag:n,projectPath:o,copyTranslations:s}=e;await kp(o,t,s),await Np(o,t),await Ap(o,t,r,i,n)},kp=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 bp(n,o);else{let s=await Ip(n,"utf-8"),c=JSON.parse(s),p=Zn(c);await $p(o,`${JSON.stringify(p,null,2)}
616
- `)}},Zn=e=>{let t={};for(let[r,i]of Object.entries(e))typeof i=="object"&&i!==null?t[r]=Zn(i):t[r]="";return t},Np=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)}},Ap=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=` {
617
- name: '${r}',
618
- locale: '${t}',
619
- flag: '${n}',
620
- country: '${i}',
621
- },
622
- `;s=s.slice(0,c)+p+s.slice(c),await h(o,s)};import{mkdir as _p,writeFile as Qr}from"node:fs/promises";import ft from"node:path";var Qn=e=>{let{componentName:t,routePath:r,hasI18n:i}=e;return i?`import { generateSEOMetadata } from '@/lib/config/seo';
623
- import type { Metadata } from 'next';
624
- import { getTranslations, setRequestLocale } from 'next-intl/server';
625
- 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}';
626
390
 
627
- type Props = {
628
- params: Promise<{ locale: string }>;
629
- };
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 c=a.length?`, { ${a.join(", ")} }`:"";return`import { describe, expect, it } from 'vitest';
630
402
 
631
- export async function generateMetadata(props: Props): Promise<Metadata> {
632
- const locale = (await props.params).locale as SupportedLocale;
633
- const t = await getTranslations({ locale, namespace: '${t}' });
403
+ import { renderWithProviders, screen } from '${n}';
404
+ import { ${t} } from '${r}';
634
405
 
635
- return generateSEOMetadata({
636
- title: t('title'),
637
- description: t('description'),
638
- path: '${r}',
406
+ describe('${t}', () => {
407
+ it('renders', () => {
408
+ renderWithProviders(<${t} />${c});
409
+ expect(screen.getByRole('heading')).toBeInTheDocument();
639
410
  });
640
- }
411
+ });
412
+ `},so=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';
641
415
 
642
- export default async function ${t}Page(props: Props) {
643
- const locale = (await props.params).locale as SupportedLocale;
644
- setRequestLocale(locale);
416
+ import { TestProviders } from '${o}';
417
+ import { ${t} } from '${r}';
645
418
 
646
- const t = await getTranslations('${t}');
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';
647
428
 
648
- return (
649
- <div>
650
- <h1>{t('title')}</h1>
651
- </div>
652
- );
653
- }
654
- `:`import type { Metadata } from 'next';
429
+ import { ${t} } from '${r}';
655
430
 
656
- export const metadata: Metadata = {
657
- title: '${t}',
658
- description: '${t} page',
659
- };
431
+ describe('${t}', () => {
432
+ it('returns a defined value', () => {
433
+ const { result } = renderHook(() => ${t}());
434
+ expect(result.current).toBeDefined();
435
+ });
436
+ });
437
+ `},io=e=>{let{camelName:t,sourceImportPath:r}=e,n=`${t}Slice`;return`import { describe, expect, it } from 'vitest';
660
438
 
661
- export default function ${t}Page() {
662
- return (
663
- <div>
664
- <h1>${t}</h1>
665
- </div>
666
- );
667
- }
668
- `},eo=e=>{let{componentName:t,paramName:r,routePath:i,hasI18n:n}=e;return n?`import { generateSEOMetadata } from '@/lib/config/seo';
669
- import type { Metadata } from 'next';
670
- import { getTranslations, setRequestLocale } from 'next-intl/server';
671
- import type { SupportedLocale } from '@/types/i18n';
439
+ import { failed, reset, ${n}, started } from '${r}';
440
+
441
+ describe('${n}', () => {
442
+ const initialState = { status: 'idle', error: null } as const;
443
+
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
+ });
672
454
 
673
- type Props = {
674
- params: Promise<{ locale: string; ${r}: string }>;
455
+ it('resets', () => {
456
+ expect(${n}.reducer({ status: 'error', error: 'boom' }, reset())).toEqual(initialState);
457
+ });
458
+ });
459
+ `};var wr=async e=>{let{name:t,outputPath:r,createStore:n,persistStore:o,createApi:s,state:i,hasI18n:a,hasTests:c}=e,l=$(t),p=s?`${l}List`:l,u=`use${l}`,f=n&&i!=="none";await yr(K.join(r,"components"),{recursive:!0}),await yr(K.join(r,"types"),{recursive:!0}),s||await yr(K.join(r,"hooks"),{recursive:!0});let d=[Ue(K.join(r,"components",`${p}.tsx`),no({componentName:p,hookName:u,withApi:s,pascalName:l,hasI18n:a})),Ue(K.join(r,"types",`${t}.types.ts`),Nt({componentName:l,withStore:f})),Ue(K.join(r,"index.ts"),Yn({featureName:t,componentName:p,hookName:u,pascalName:l,withStore:f,withApi:s}))];s||d.push(Ue(K.join(r,"hooks",`${u}.ts`),f?oo({hookName:u,componentName:l,featureName:t,state:i==="zustand"?"zustand":"redux"}):Ft({hookName:u}))),s&&(d.push(st({name:t,featurePath:r,withActions:!0})),d.push(Ue(K.join(r,"server.ts"),Qn({pascalName:l})))),c&&d.push(Ue(K.join(r,"components",`${p}.test.tsx`),Mt({componentName:p,sourceImportPath:`./${p}`,testUtilsImportPath:"../../../../test/test-utils",hasState:i!=="none",hasI18n:a,withQueryData:s?{keysImport:"../api/keys",keys:`${ka(t)}Keys`,key:"list"}:void 0}))),await Promise.all(d),f&&await it({name:t,outputPath:K.join(r,"store"),persist:o,state:i==="zustand"?"zustand":"redux",typesImportPath:`../types/${t}.types`})},ka=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase());import{mkdir as Pa,writeFile as Sa}from"node:fs/promises";import xr from"node:path";var br=async e=>{let{name:t,projectPath:r,featurePath:n}=e,o=`use${$(t)}`,s=n?xr.join(r,n,"hooks"):xr.join(r,"src","hooks");await Pa(s,{recursive:!0}),await Sa(xr.join(s,`${o}.ts`),Ft({hookName:o}))};import{mkdir as Ea}from"node:fs/promises";import _t from"node:path";var ao=({componentName:e})=>`type Props = {
460
+ children: React.ReactNode;
675
461
  };
676
462
 
677
- export async function generateMetadata(props: Props): Promise<Metadata> {
678
- const { locale, ${r} } = await props.params;
679
- 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 Ta=/^[a-z][a-z0-9-]*$/,Ra=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,ja=e=>{if(!Ta.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.`)},Ca=e=>{if(!Ra.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},vr=async e=>{let{segment:t,at:r,projectPath:n,hasI18n:o,withGroup:s}=e;ja(t),r&&Ca(r);let i=`${$(t)}Layout`,a=s?`(${t})`:t,c=["src","app"];o&&c.push("[locale]"),r&&c.push(...r.split("/")),c.push(a);let l=_t.join(n,...c),p=_t.join(l,"layout.tsx");if(w(p))throw new Error(`Layout already exists at ${_t.relative(n,p)}.`);return await Ea(l,{recursive:!0}),await P(p,ao({componentName:i,hasI18n:o})),{layoutFile:p,displayPath:_t.relative(n,p),componentName:i}};import{copyFile as Oa,readFile as Aa,writeFile as Ia}from"node:fs/promises";import ct from"node:path";var $r=async e=>{let{code:t,name:r,country:n,flag:o,projectPath:s,copyTranslations:i}=e;await Na(s,t,i),await Fa(s,t),await Ma(s,t,r,n,o)},Na=async(e,t,r)=>{let n=ct.join(e,"src","i18n","translations"),o=ct.join(n,"en.json"),s=ct.join(n,`${t}.json`);if(r)await Oa(o,s);else{let i=await Aa(o,"utf-8"),a=JSON.parse(i),c=co(a);await Ia(s,`${JSON.stringify(c,null,2)}
469
+ `)}},co=e=>{let t={};for(let[r,n]of Object.entries(e))typeof n=="object"&&n!==null?t[r]=co(n):t[r]="";return t},Fa=async(e,t)=>{let r=ct.join(e,"src","types","i18n.ts");if(!w(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 c=s[1].split(",").map(l=>l.trim()).filter(Boolean);c.push(`'${t}'`),n=n.replace(o,`export const SUPPORTED_LOCALES = [${c.join(", ")}] as const;`),await P(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 P(r,n))},Ma=async(e,t,r,n,o)=>{let s=ct.join(e,"src","lib","config","app-locales.ts");if(!w(s))return;let i=await j(s);if(i.includes(`locale: '${t}'`))return;let a=i.lastIndexOf("];");if(a===-1)return;let c=` {
470
+ name: '${r}',
471
+ locale: '${t}',
472
+ flag: '${o}',
473
+ country: '${n}',
474
+ ogLocale: '${t}_${t.toUpperCase()}',
475
+ },
476
+ `;i=i.slice(0,a)+c+i.slice(a),await P(s,i)};import{mkdir as Da,writeFile as kr}from"node:fs/promises";import Dt from"node:path";var _a=({routePath:e,hasI18n:t,paramName:r})=>`${t?"/[locale]":""}${e}${r?`/[${r}]`:""}`,po=e=>{let{componentName:t,routePath:r,hasI18n:n,paramName:o}=e,s=_a(e);return n?`import type { Metadata } from 'next';
477
+ import { getLocale, getTranslations } from 'next-intl/server';
680
478
 
479
+ import { generateSEOMetadata } from '@/lib/config/seo';
480
+
481
+ export async function generateMetadata(): Promise<Metadata> {
482
+ const [locale, t] = await Promise.all([getLocale(), getTranslations('${t}')]);
681
483
  return generateSEOMetadata({
682
484
  title: t('title'),
683
485
  description: t('description'),
684
- path: '${i}',
486
+ path: '${r}',
487
+ locale,
685
488
  });
686
489
  }
687
490
 
688
- export default async function ${t}Page(props: Props) {
689
- const { locale, ${r} } = await props.params;
690
- setRequestLocale(locale);
691
-
491
+ export default async function ${t}Page(${o?`{ params }: PageProps<'${s}'>`:""}) {
692
492
  const t = await getTranslations('${t}');
693
-
493
+ ${o?` const { ${o} } = await params;
494
+ `:""}
694
495
  return (
695
- <div>
696
- <h1>{t('title')}</h1>
697
- <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>`:""}
698
500
  </div>
699
501
  );
700
502
  }
701
503
  `:`import type { Metadata } from 'next';
702
504
 
703
- export const metadata: Metadata = {
505
+ import { generateSEOMetadata } from '@/lib/config/seo';
506
+
507
+ export const metadata: Metadata = generateSEOMetadata({
704
508
  title: '${t}',
705
509
  description: '${t} page',
706
- };
707
-
708
- type Props = {
709
- params: Promise<{ ${r}: string }>;
710
- };
510
+ path: '${r}',
511
+ });
711
512
 
712
- export default async function ${t}Page(props: Props) {
713
- const { ${r} } = await props.params;
513
+ export default ${o?"async ":""}function ${t}Page(${o?`{ params }: PageProps<'${s}'>`:""}) {
514
+ ${o?` const { ${o} } = await params;
714
515
 
715
- return (
716
- <div>
717
- <h1>${t}</h1>
718
- <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>`:""}
719
520
  </div>
720
521
  );
721
522
  }
722
- `},to=e=>`export default function ${e.componentName}Loading() {
523
+ `},lo=e=>`export default function ${e.componentName}Loading() {
723
524
  return (
724
- <div className="flex h-full items-center justify-center">
725
- <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>
726
527
  </div>
727
528
  );
728
529
  }
729
- `,ro=e=>`'use client';
530
+ `,mo=e=>`'use client';
531
+
532
+ import { useEffect } from 'react';
533
+
534
+ import { logger } from '@/lib/logger';
730
535
 
731
536
  export default function ${e.componentName}Error({
732
537
  error,
@@ -735,481 +540,419 @@ export default function ${e.componentName}Error({
735
540
  error: Error & { digest?: string };
736
541
  reset: () => void;
737
542
  }) {
543
+ useEffect(() => {
544
+ logger.error({ err: error, digest: error.digest }, '${e.componentName} failed to render');
545
+ }, [error]);
546
+
738
547
  return (
739
- <div className="flex h-full flex-col items-center justify-center gap-4">
740
- <h2>Something went wrong!</h2>
741
- <p>{error.message}</p>
742
- <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>
743
557
  </div>
744
558
  );
745
559
  }
746
- `;var ei=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 _p(d,{recursive:!0});let f=n?eo({componentName:c,routePath:n?`${p}/[${n}]`:p,paramName:n,hasI18n:i}):Qn({componentName:c,routePath:p,hasI18n:i});await Qr(ft.join(d,"page.tsx"),f),o&&await Qr(ft.join(d,"loading.tsx"),to({componentName:c})),s&&await Qr(ft.join(d,"error.tsx"),ro({componentName:c}))};P();import{mkdir as Fp}from"node:fs/promises";import ti from"node:path";w();var io=(e,t)=>e.endsWith(t)?e:`${e}${t}`,no=({baseName:e})=>{let t=io(e,"Provider"),r=t.replace(/Provider$/,""),i=`${r}ContextValue`,n=`${r}Context`,o=`use${r}`;return`'use client';
560
+ `;var Pr=async e=>{let{name:t,projectPath:r,hasI18n:n,group:o,dynamic:s,withLoading:i=!1,withError:a=!1}=e,c=$(t),l=`/${t}`,p=[r,"src","app"];n&&p.push("[locale]"),o&&p.push(`(${o})`),p.push(t),s&&p.push(`[${s}]`);let u=Dt.join(...p);return await Da(u,{recursive:!0}),await kr(Dt.join(u,"page.tsx"),po({componentName:c,routePath:l,hasI18n:n,paramName:s})),i&&await kr(Dt.join(u,"loading.tsx"),lo({componentName:c})),a&&await kr(Dt.join(u,"error.tsx"),mo({componentName:c})),{pageDir:u,routePath:s?`${l}/[${s}]`:l}};import{mkdir as La}from"node:fs/promises";import Sr from"node:path";var uo=(e,t)=>e.endsWith(t)?e:`${e}${t}`,fo=({baseName:e})=>{let t=uo(e,"Provider"),r=t.replace(/Provider$/,""),n=`${r}ContextValue`,o=`${r}Context`,s=`use${r}`;return`'use client';
747
561
  import { createContext, useContext, useMemo } from 'react';
748
562
 
749
- type ${i} = {
563
+ type ${n} = {
750
564
  // TODO: declare context value fields
751
565
  };
752
566
 
753
- const ${n} = createContext<${i} | null>(null);
567
+ const ${o} = createContext<${n} | null>(null);
754
568
 
755
- export const ${o} = (): ${i} => {
756
- const ctx = useContext(${n});
569
+ export const ${s} = (): ${n} => {
570
+ const ctx = useContext(${o});
757
571
  if (!ctx) {
758
- throw new Error('${o} must be used within ${t}');
572
+ throw new Error('${s} must be used within ${t}');
759
573
  }
760
574
  return ctx;
761
575
  };
762
576
 
763
577
  export const ${t} = ({ children }: { children: React.ReactNode }) => {
764
- const value = useMemo<${i}>(() => ({}), []);
765
- return <${n}.Provider value={value}>{children}</${n}.Provider>;
578
+ const value = useMemo<${n}>(() => ({}), []);
579
+ return <${o}.Provider value={value}>{children}</${o}.Provider>;
766
580
  };
767
- `},oo=e=>io(e,"Provider");var Op=/^[A-Za-z][A-Za-z0-9-]*$/,Dp=e=>e.includes("-")?N(e):e.charAt(0).toUpperCase()+e.slice(1),ri=async e=>{let{name:t,projectPath:r}=e;if(!Op.test(t))throw new Error(`Invalid provider name "${t}". Use kebab-case or PascalCase letters and digits only.`);let i=Dp(t).replace(/Provider$/,""),n=oo(i),o=n,s=ti.join(r,a.PROVIDERS),c=ti.join(s,`${o}.tsx`);if(l(c))throw new Error(`Provider already exists at ${ti.relative(r,c)}.`);return await Fp(s,{recursive:!0}),await h(c,no({baseName:i})),{componentName:n,fileBaseName:o,providerFile:c}};w();import gt from"node:path";var so=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?`, {
768
- ${s.join(`,
769
- `)},
770
- }`:"";return`import { describe, expect, it } from 'vitest';
771
- import { renderWithProviders, screen } from '${i}';
772
- import { ${t} } from '${r}';
773
-
774
- describe('${t}', () => {
775
- it('renders without crashing', () => {
776
- renderWithProviders(<${t} />${c});
777
- // TODO: assert something meaningful about the rendered output.
778
- expect(screen).toBeDefined();
779
- });
780
- });
781
- `},ao=e=>{let{hookName:t,sourceImportPath:r,withStore:i,testUtilsImportPath:n}=e;return i?`import { renderHook } from '@testing-library/react';
782
- import { describe, expect, it } from 'vitest';
783
- import { TestProviders } from '${n}';
784
- import { ${t} } from '${r}';
785
-
786
- describe('${t}', () => {
787
- it('returns a defined value', () => {
788
- const { result } = renderHook(() => ${t}(), { wrapper: TestProviders });
789
- expect(result.current).toBeDefined();
790
- });
791
- });
792
- `:`import { renderHook } from '@testing-library/react';
793
- import { describe, expect, it } from 'vitest';
794
- import { ${t} } from '${r}';
795
-
796
- describe('${t}', () => {
797
- it('returns a defined value', () => {
798
- const { result } = renderHook(() => ${t}());
799
- expect(result.current).toBeDefined();
800
- });
801
- });
802
- `},co=e=>{let{camelName:t,sourceImportPath:r}=e,i=`${t}Slice`;return`import { describe, expect, it } from 'vitest';
803
- import { ${i}, resetState, setError, setLoading } from '${r}';
804
-
805
- describe('${i}', () => {
806
- const initialState = { loading: false, error: null } as const;
807
-
808
- it('returns the initial state', () => {
809
- expect(${i}.reducer(undefined, { type: '@@INIT' })).toEqual(initialState);
810
- });
811
-
812
- it('handles setLoading', () => {
813
- const next = ${i}.reducer(initialState, setLoading(true));
814
- expect(next.loading).toBe(true);
815
- });
816
-
817
- it('handles setError', () => {
818
- const next = ${i}.reducer(initialState, setError('boom'));
819
- expect(next.error).toBe('boom');
820
- });
821
-
822
- it('handles resetState', () => {
823
- const next = ${i}.reducer({ loading: true, error: 'boom' }, resetState());
824
- expect(next).toEqual(initialState);
825
- });
826
- });
827
- `};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=jp(o,gt.join(t,"test","test-utils")),f;return i==="component"?f=so({componentName:n,sourceImportPath:u,testUtilsImportPath:d,hasRedux:!!e.hasRedux,hasI18n:!!e.hasI18n}):i==="hook"?f=ao({hookName:n,sourceImportPath:u,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?d:void 0}):f=co({camelName:n,sourceImportPath:u}),await h(p,f),p},jp=(e,t)=>{let r=gt.relative(e,t).replace(/\\/g,"/");return r.startsWith(".")?r:`./${r}`};import Lp from"enquirer";var{prompt:Mp}=Lp,po=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 Mp(r):{};return{componentName:e??i.componentName,isClient:t.client??i.isClient??!1}};var lo=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
+ `},go=e=>uo(e,"Provider");var Ua=/^[A-Za-z][A-Za-z0-9-]*$/,Ga=e=>e.includes("-")?$(e):e.charAt(0).toUpperCase()+e.slice(1),Er=async e=>{let{name:t,projectPath:r}=e;if(!Ua.test(t))throw new Error(`Invalid provider name "${t}". Use kebab-case or PascalCase letters and digits only.`);let n=Ga(t).replace(/Provider$/,""),o=go(n),s=o,i=Sr.join(r,C.PROVIDERS),a=Sr.join(i,`${s}.tsx`);if(w(a))throw new Error(`Provider already exists at ${Sr.relative(r,a)}.`);return await La(i,{recursive:!0}),await P(a,fo({baseName:n})),{componentName:o,fileBaseName:s,providerFile:a}};import pt from"node:path";var oe=async e=>{let{projectPath:t,sourceFile:r,kind:n,symbolName:o}=e,s=pt.dirname(r),i=pt.basename(r).replace(/\.(tsx?|jsx?)$/i,""),a=n==="component"?".tsx":".ts",c=pt.join(s,`${i}.test${a}`),l=`./${i}`,p=Ba(s,pt.join(t,"test","test-utils")),u;return n==="component"?u=Mt({componentName:o,sourceImportPath:l,testUtilsImportPath:p,hasState:!!e.hasState,hasI18n:!!e.hasI18n}):n==="hook"?u=so({hookName:o,sourceImportPath:l,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?p:void 0}):u=io({camelName:o,sourceImportPath:l}),await P(c,u),c},Ba=(e,t)=>{let r=pt.relative(e,t).replace(/\\/g,"/");return r.startsWith(".")?r:`./${r}`};import{readFile as Va,writeFile as Ha}from"node:fs/promises";import qa from"node:path";var Ge=(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}`},Tr=(e,t)=>{let r=/combineSlices\(([\s\S]*?)\)/,n=e.match(r);if(!n)throw new Error("Could not find combineSlices in rootReducer.ts");let o=n[1].split(",").map(c=>c.trim()).filter(Boolean);if(o.includes(t))return e;let s=o.indexOf("persistSlice");s===-1?o.push(t):o.splice(s,0,t);let i=`combineSlices(${o.join(", ")})`,a=`combineSlices(
584
+ ${o.join(`,
585
+ `)},
586
+ )`;return e.replace(r,i.length<=100?i:a)},Rr=(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(", ")}]`))},jr=(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 lt=async e=>{let{serviceName:t,projectPath:r}=e,n=G(t),o=qa.join(r,"src","lib","config","app-apis.ts");try{let s=await Va(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=jr(s,Wn({kebabName:t,camelName:n,pascalName:$(t)})),await Ha(o,s)}catch(s){throw new Error(`Failed to register API endpoints: ${s}`,{cause:s})}};import{readFile as Ja,writeFile as za}from"node:fs/promises";import Wa from"node:path";var Cr=async(e,t,r)=>{let n=Wa.join(e,"src","lib","config","app-paths.ts");if(!w(n))return;let o=await Ja(n,"utf-8"),s=G(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 za(n,o)};import Or from"node:path";var Ka=/^[A-Z][A-Z0-9_]*$/,wo=e=>{if(!Ka.test(e))throw new Error(`Invalid env var name "${e}". Use UPPER_SNAKE_CASE (e.g. SENTRY_DSN, NEXT_PUBLIC_API_URL).`)},se=(e,t)=>t?e.startsWith("NEXT_PUBLIC_")?e:`NEXT_PUBLIC_${e}`:e,Ar=e=>e.public?"client":"server",Xa=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,"\\'")}')`}},Ya=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()"}},xo=e=>{let t=se(e.name,e.public),r=Ya(e),n=e.default!==void 0,o=!e.required&&!n;if(n&&(r+=Xa(e)),o&&(r+=".optional()"),e.description){let s=e.description.replace(/'/g,"\\'");r+=`
590
+ .describe('${s}')`}return` ${t}: ${r},`},bo=e=>{let t=se(e.name,e.public);return` ${t}: process.env.${t},`},Qa=e=>new RegExp(`(${e}:\\s*\\{)([\\s\\S]*?)(\\n\\s*\\})`),ho=/(runtimeEnv:\s*\{)([\s\S]*?)(\n\s*\})/,yo=(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/,"")}`},vo=(e,t)=>{let r=se(t.name,t.public),n=Ar(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=Qa(n),i=e.match(s);if(!i)throw new Error(`Could not locate the \`${n}: { \u2026 }\` group in src/lib/env/index.ts.`);let[,a,c,l]=i,p=xo(t),u=e.replace(s,yo(a,c,l,p)),f=u.match(ho);if(!f)throw new Error("Could not locate the `runtimeEnv: { \u2026 }` map in src/lib/env/index.ts.");let[,d,g,y]=f;return u=u.replace(ho,yo(d,g,y,bo(t))),u},Za="# -public",ec=e=>{let t=se(e.name,e.public),r=e.default??"",n=e.public?` ${Za}`:"";return`${t}=${r}${n}`},$o=(e,t)=>{let r=se(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}${ec(t)}
597
+ `},ko=(e,t)=>{let r=se(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
+ `},Ir=async(e,t)=>{wo(t.name);let r=Or.join(e,"src/lib/env/index.ts");if(!w(r))throw new Error("src/lib/env/index.ts not found \u2014 run `next-maker init` first.");let n=await j(r),o=vo(n,t),s=o!==n;s&&await P(r,o);let i=Or.join(e,C.ENV_EXAMPLE),a=!1;if(w(i)){let p=await j(i),u=$o(p,t);a=u!==p,a&&await P(i,u)}let c=Or.join(e,".env"),l=!1;if(w(c)){let p=await j(c),u=ko(p,t);l=u!==p,l&&await P(c,u)}return{schemaUpdated:s,envExampleUpdated:a,envUpdated:l,group:Ar(t)}};import Lt from"node:path";var tc=e=>new RegExp(`<\\s*${e}\\b`),Po=(e,t)=>{if(tc(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)},So=(e,t)=>{let r=`export * from './${t}';`;if(e.includes(r))return e;let n=e.split(`
603
+ `),o=[],s=[];for(let u of n)/^export \* from '\.\/.*';\s*$/.test(u)?o.push(u):s.push(u);let i=[...o,r].sort(),a=n.findIndex(u=>/^export \* from '\.\//.test(u));if(a===-1)return`${r}
604
+ ${e.replace(/^\n+/,"")}`;let c=n.slice(0,a),l=n.findIndex((u,f)=>f>a&&!/^export \* from '\.\//.test(u)),p=l===-1?[]:n.slice(l);return[...c,...i,...p].join(`
605
+ `)},rc=C.PROVIDERS,nc=C.ROOT_PROVIDER,oc=C.PROVIDERS_INDEX,Eo=async(e,t={fileExists:w,readFile:j})=>{let r=Lt.join(e,nc);if(t.fileExists(r))return r;let n=Lt.join(e,rc);if(!t.fileExists(n))return null;let{readdir:o}=await import("node:fs/promises"),i=(await o(n).catch(()=>[])).filter(c=>c.endsWith(".tsx")),a=null;for(let c of i){let l=Lt.join(n,c),p=await t.readFile(l);if(!/\{children\}/.test(p))continue;let f=((p.split("{children}")[0]??"").match(/<[A-Z][A-Za-z0-9_]*\b/g)??[]).length;(!a||f>a.depth)&&(a={file:l,depth:f})}return a?.file??null},Nr=async e=>{let{projectPath:t,componentName:r,fileBaseName:n}=e,o=await Eo(t);if(o){let a=await j(o),c=a;(!/from '\.\//.test(a)||!a.includes(`{ ${r} }`))&&(c=Ge(c,`import { ${r} } from './${n}';`)),c=Po(c,r),c!==a&&await P(o,c)}let s=Lt.join(t,oc),i=null;if(w(s)){let a=await j(s),c=So(a,n);c!==a&&await P(s,c),i=s}return{rootProviderFile:o,barrelFile:i}};import{readFile as To,writeFile as Ro}from"node:fs/promises";import jo from"node:path";var mt=async e=>{let{projectPath:t,name:r,persist:n,importPath:o}=e,s=jo.join(t,"src","store","rootReducer.ts"),i=jo.join(t,"src","store","index.ts"),a=G(r),c=o.replace(/^src\//,"@/");try{let l=await To(s,"utf-8");if(l=Ge(l,`import { ${a}Slice } from '${c}';`),l=Tr(l,`${a}Slice`),await Ro(s,l),n&&w(i)){let p=await To(i,"utf-8");p=Ge(p,`import { ${a}Persistence } from '${c}';`),p=Rr(p,`${a}Persistence`),await Ro(i,p)}}catch(l){throw new Error(`Failed to register slice '${r}': ${l}`,{cause:l})}};import{readFile as sc,writeFile as ic}from"node:fs/promises";import ac from"node:path";var Te=async(e,t)=>{let r=ac.join(e,"src","i18n","translations","en.json");if(!w(r))return;let n=await sc(r,"utf-8"),o=JSON.parse(n);o[t]||(o[t]={title:t,description:`${t} page description`},await ic(r,`${JSON.stringify(o,null,2)}
606
+ `))};import cc from"enquirer";var{prompt:pc}=cc,Co=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 pc(r):{};return{componentName:e??n.componentName,isClient:t.client??n.isClient??!1}};var Oo=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(),o=Date.now();m(X.cyan(`
828
607
  \u{1F9E9} Component Generator
829
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected");let o=await po(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=Gp(r,n.hasTests);y.start("Generating component..."),await Jr({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?ii.join(i,r.feature,"components",`${s}.tsx`):ii.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(`
830
- \u2728 Component '${s}' created successfully!
831
- `)),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} ${ii.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)}})},Gp=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import $ from"picocolors";P();w();import Jp from"node:path";P();import uo from"node:path";import Hp from"picocolors";w();var mo=/export default (.*?);/,ni="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';
832
- ${e}`,r=t.match(mo);if(!r)throw new Error("Could not locate `export default` in next.config.ts.");let n=`const bundleAnalyzer = withBundleAnalyzer({
833
- enabled: process.env.ANALYZE === 'true',
834
- });
835
-
836
- export default bundleAnalyzer(${r[1]});`;return t.replace(mo,n)};var Wt=async e=>{let t=S("Adding @next/bundle-analyzer...");try{let r=uo.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 _(uo.join(e,a.PACKAGE_JSON),n=>(n.scripts=n.scripts??{},n.scripts.analyze||(n.scripts.analyze=ni),n)),t.text="Installing @next/bundle-analyzer...",await At(e,"@next/bundle-analyzer"),t.succeed(Hp.green("@next/bundle-analyzer configured."))}catch(r){throw t.fail("Failed to add @next/bundle-analyzer."),r}};P();import zp from"node:path";import Vp from"picocolors";w();P();import Up from"node:path";w();var qt=(e,t)=>e.filter(r=>r.kind==="missingPackage"&&r.depKind===t).map(r=>r.name),Bp=e=>e.filter(t=>t.kind==="missingFile").map(t=>t.file),F=(e,t)=>Bp(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 $r(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=Up.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 fo=async(e,t)=>{let r=S("Repairing Bundle Analyzer...");try{if(U(t,a.NEXT_CONFIG)){let s=zp.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(Vp.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 oi=(e,t)=>{let r=e.split(`
837
- `),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(`
838
- `)},Ve=e=>t=>{let r=t.split(`
839
- `),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(`
840
- `);return oi(d,e)};var go=e=>{let t=e.split(`
841
- `).filter(i=>!/^import\s+createNextIntlPlugin\s+from\s+['"]next-intl\/plugin['"];?\s*$/.test(i)).join(`
842
- `);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},ho=e=>{let t=oi(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 yo={id:"bundle-analyzer",name:"Bundle Analyzer",description:"@next/bundle-analyzer wrapped around the default export",detect:async e=>{let t=Jp.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:ho}],apply:O(Wt,fo)};P();w();import Yp from"node:path";P();import xo from"node:path";import Wp from"picocolors";w();var yt=`${JSON.stringify({path:"cz-conventional-changelog"},null,2)}
843
- `;var si=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}},ai=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=xo.join(e,a.CZRC);l(r)||await h(r,yt);let i=xo.join(e,a.PACKAGE_JSON),n=JSON.parse(await g(i)),o=ai(n);if(await _(i,s=>si(s)),o.length>0){t.text=`Installing ${o.join(", ")}...`;let s=await A(e);await fe(e,s,o)}t.succeed(Wp.green("Commitizen configured. Use `commit` to start a guided commit."))}catch(r){throw t.fail("Failed to set up Commitizen."),r}};P();import qp from"node:path";import Xp from"picocolors";w();var wo=async(e,t)=>{let r=S("Repairing Commitizen...");try{let i=!1;if(F(t,a.CZRC)){let c=qp.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(Xp.green(s.length>0?`Commitizen repaired (${s.join("; ")}).`:"Commitizen repaired."))}catch(i){throw r.fail("Failed to repair Commitizen."),i}};var Po={id:"commitizen",name:"Commitizen",description:"Guided conventional-commit prompt via `commit` script",detect:async e=>l(Yp.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,wo)};P();w();import tl from"node:path";import Kp from"node:path";import Zp from"picocolors";w();P();import vo from"node:path";w();var So=async(e,t)=>{t.text="Fetching assets from starter repo...",await J(e)},Eo=async(e,t)=>{let r=vo.join(e,a.THEME_PROVIDER),i=vo.join(t,"src/providers/CustomThemeProvider.tsx");await b(i,r)};P();w();import xt from"node:path";P();w();import To from"node:path";var Yt=async e=>{let t=[To.join(e,a.ROOT_LAYOUT),To.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 Co=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:""}},Ro=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';
844
- `,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';
845
- ${r}`),n&&(r=`${i}
846
- ${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 (
847
- <CustomThemeProvider>
848
- ${o[1]}
849
- </CustomThemeProvider>
850
- );`))}await h(t,r)},bo=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+=`
851
- ${c[0]}
852
- `),p&&(n+=`
853
- ${p[0]}
854
- `)}n||(n=`
855
- @custom-variant dark (&:where(.dark, .dark *));
856
-
857
- @theme {
858
- --color-dark: #202938;
859
- --color-light: #f5f5f5;
860
- }
861
- `);let o=await g(r);if(!o.includes("@custom-variant dark")){if(o.includes("@import")){let s=o.lastIndexOf("@import"),c=o.indexOf(`
862
- `,s);o=o.slice(0,c+1)+n+o.slice(c+1)}else o=n+o;await h(r,o)}},Io=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=Kp.join(e,".next-maker-temp");try{let{isSetup:i,reason:n}=await Co(e);if(i){t.fail(`Dark Theme is already set up (${n}).`);return}let o=await Ro(e);await So(r,t),await Eo(e,r),await Zt(e),await Qt(e),await bo(e,r),await Io(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(Zp.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 Qp from"picocolors";w();var el="src/providers/RootProvider.tsx",$o=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,el)&&(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(Qp.green(c.length>0?`Dark Theme repaired (${c.join("; ")}).`:"Dark Theme repaired."))}catch(n){throw r.fail("Failed to repair Dark Theme."),n}};var ko={id:"dark-theme",name:"Dark Theme",description:"@teispace/next-themes + CustomThemeProvider",detect:async e=>l(tl.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,$o)};import rl from"node:fs/promises";import ir from"node:path";import nr from"enquirer";import ci from"picocolors";w();P();import re from"node:fs/promises";import T from"node:path";w();var No=async(e,t)=>{t.text="Downloading assets from starter repository...",await J(e)},Ao=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+=`
863
- export const API_RESPONSE_DATA_KEY = 'data';
864
- `,f=!0),d.includes("SAVE_AUTH_TOKENS")||(d+=`
865
- export const SAVE_AUTH_TOKENS = false;
866
- `,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))},_o=async e=>{let{isFileUsed:t,isStringUsed:r,stripSaveAuthTokens:i}=await Promise.resolve().then(()=>(Be(),nn));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,`
867
-
608
+ `)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected");let i=await Co(t,{client:r.client});M(i.componentName,"component name"),r.feature&&W(r.feature,"feature path");let a=$(i.componentName),c=i.isClient,l=r.i18n??!1,p=lc(r,s.hasTests);h.start("Generating component..."),await fr({name:i.componentName,projectPath:n,isClient:c||l,hasI18n:l,featurePath:r.feature}),h.succeed("Component generated"),l&&await Te(n,a);let u=null;if(p){let d=r.feature?Fr.join(n,r.feature,"components",`${a}.tsx`):Fr.join(n,"src/components/common",a,`${a}.tsx`);h.start("Generating test..."),u=await oe({projectPath:n,sourceFile:d,kind:"component",symbolName:a,hasState:s.state!=="none",hasI18n:s.hasI18n}),h.succeed("Test generated")}let f=r.feature?`${r.feature}/components/${a}.tsx`:`src/components/common/${a}/${a}.tsx`;await k(n,o),m(X.green(`
609
+ \u2728 Component '${a}' created successfully!
610
+ `)),m(X.dim(` \u{1F4C4} ${f}`)),r.feature||(m(X.dim(` \u{1F4C4} src/components/common/${a}/index.ts`)),m(X.dim(" \u{1F4DD} Updated src/components/common/index.ts")),m(X.dim(" \u{1F4DD} Updated src/components/index.ts"))),u&&m(X.dim(` \u{1F9EA} ${Fr.relative(n,u)}`)),m(""),m(X.cyan("Usage:")),r.feature?m(X.dim(` import { ${a} } from '${r.feature.replace(/^src\//,"@/")}/components/${a}';`)):m(X.dim(` import { ${a} } from '@/components';`)),m("")}catch(n){h.fail("Component generation failed"),b(`${n}`),process.exit(1)}})},lc=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import{spawnSync as mc}from"node:child_process";import _ from"picocolors";var uc=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}`}},Ao=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 re(r),s=await De(o,{starterPath:t.starterPath});n=s.dispose;let i=await ir(r,o,s),a=0;if(t.fix&&i.some(p=>p.drift.length)){let p=await In(r,{...o,answers:{}},s,o.answers),u=new Set(i.flatMap(g=>g.drift.filter(y=>y.kind==="missingFile").map(y=>y.file)));p.copy=p.copy.filter(g=>u.has(g)),p.remove=[],p.unwrapJsx=[],p.unwrapCall=[],p.removePackages=[],p.removeScripts=[],p.manual=[];let f=new Set(i.flatMap(g=>g.drift.filter(y=>y.kind==="missingPackage").map(y=>y.name)));for(let g of Object.keys(p.addPackages))f.has(g)||delete p.addPackages[g];for(let g of Object.keys(p.addDevPackages))f.has(g)||delete p.addDevPackages[g];let d=new Set(i.flatMap(g=>g.drift.filter(y=>y.kind==="missingScript").map(y=>y.name)));for(let g of Object.keys(p.addScripts))d.has(g)||delete p.addScripts[g];await Nn(r,s,p,o),a=p.copy.length+Object.keys(p.addPackages).length+Object.keys(p.addDevPackages).length+Object.keys(p.addScripts).length,i=await ir(r,o,s)}let c;if(t.compile){let[p,...u]=ce(o.packageManager,"type-check").split(" "),f=mc(p,u,{cwd:r,encoding:"utf-8"});c={ok:f.status===0,output:`${f.stdout}${f.stderr}`.trim()}}let l=i.filter(p=>p.on&&p.drift.length);t.json&&(m(JSON.stringify({starter:o.starter,results:i,fixed:a,compile:c},null,2)),process.exit(l.length||c?.ok===!1?1:0)),m(_.cyan(`
611
+ \u{1FA7A} Project Doctor
612
+ `)),m(_.dim(` ${o.starter.name} ${o.starter.version} \xB7 ${s.source.location}
613
+ `));for(let p of i){if(!p.on){m(` ${_.dim("\u2014")} ${_.dim(p.id)} ${_.dim("(off)")}`);continue}if(p.drift.length===0){m(` ${_.green("\u2713")} ${p.id}`);continue}m(` ${_.yellow("!")} ${_.bold(p.id)} ${_.dim(`(${p.drift.length} issue${p.drift.length===1?"":"s"})`)}`);for(let u of p.drift)m(` ${_.yellow("\u2022")} ${uc(u)}`)}a&&m(_.green(`
614
+ restored ${a} item(s) from the starter`)),c&&(m(""),m(c.ok?_.green(" \u2713 type-check passed"):_.red(" \u2717 type-check failed")),c.ok||m(_.dim(c.output.split(`
615
+ `).slice(-30).join(`
616
+ `)))),m(""),l.length===0&&c?.ok!==!1&&(m(_.green(`\u2713 No drift detected.
617
+ `)),process.exit(0)),l.length&&!t.fix&&m(_.dim(`Run with --fix to restore missing pieces from the starter.
618
+ `)),process.exit(1)}catch(o){b(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{await n?.()}})};import Be from"picocolors";var Io=new Set(["string","url","number","boolean","enum"]),dc=e=>{let t=(e??"string").toLowerCase();if(!Io.has(t))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(Io).join(", ")}.`);return t},fc=e=>{if(!e)return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length===0?void 0:t},No=e=>{e.command("env <NAME>").description("Declare a new environment variable across src/lib/env/index.ts, .env.example, and .env").option("--type <type>","Coercer type (string|url|number|boolean|enum)","string").option("--required","Make the variable required (no .optional() / .default())").option("--default <value>","Set a default value (mutually exclusive with --required)").option("--public","Mark as public: place in the client group, auto-prefix NEXT_PUBLIC_, tag .env.example").option("--describe <text>","Description (rendered as .describe() and .env.example comment)").option("--enum <list>",'Comma-separated values when --type=enum (e.g. "dev,prod")').action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();if(m(Be.cyan(`
619
+ \u{1F510} Env Var Generator
620
+ `)),r.required&&r.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let s=dc(r.type),i=fc(r.enum);if(s==="enum"&&(!i||i.length===0))throw new Error("--type=enum requires at least one value via --enum a,b,c.");let a={name:t,type:s,required:!!r.required,default:r.default,description:r.describe,enumValues:i,public:!!r.public};h.start("Updating src/lib/env/index.ts, .env.example, and .env...");let c=await Ir(n,a);h.succeed("Env var written");let l=se(a.name,a.public);await k(n,o),m(Be.green(`
621
+ \u2728 ${l} declared in the ${c.group} group.
622
+ `)),m(Be.dim("Touched:")),m(Be.dim(` ${c.schemaUpdated?"\u270F\uFE0F ":"\u23ED "} src/lib/env/index.ts`)),m(Be.dim(` ${c.envExampleUpdated?"\u270F\uFE0F ":"\u23ED "} .env.example`)),m(Be.dim(` ${c.envUpdated?"\u270F\uFE0F ":"\u23ED "} .env`)),m("")}catch(n){h.fail("Env var declaration failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Bt from"node:path";import je from"picocolors";import gc from"enquirer";var{prompt:hc}=gc,Fo=async(e={})=>e.source?{source:e.source}:{source:(await hc({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 yc,readFile as wc,stat as xc}from"node:fs/promises";import Re from"node:path";var Mo=async e=>{try{return await yc(e),!0}catch{return!1}},bc=async e=>{try{return(await xc(e)).isDirectory()}catch{return!1}},Ut=async e=>{let t=[Re.join(e,"src","app"),Re.join(e,"app")];for(let r of t)if(await bc(r))return r;return null},vc=["next-pwa","@ducanh2912/next-pwa","@serwist/next","serwist"],Mr=async e=>{let t={isPwa:!1},r=[Re.join(e,"public","manifest.json"),Re.join(e,"public","manifest.webmanifest"),Re.join(e,"public","site.webmanifest")];for(let o of r)if(await Mo(o)){t.publicManifestPath=o,t.isPwa=!0;break}let n=await Ut(e);if(n)for(let o of["ts","tsx","js"]){let s=Re.join(n,`manifest.${o}`);if(await Mo(s)){t.appManifestPath=s,t.isPwa=!0;break}}try{let o=JSON.parse(await wc(Re.join(e,"package.json"),"utf-8")),s={...o.dependencies,...o.devDependencies};for(let i of vc)if(s[i]){t.framework=i,t.isPwa=!0;break}}catch{}return t};import{mkdir as Ec,readFile as Jo,stat as Tc,writeFile as Rc}from"node:fs/promises";import Y from"node:path";import jc from"sharp";import kc from"png-to-ico";import ut from"sharp";var _o=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),_r=(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 $c(t)}},$c=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="${_o(n)}" fill="white"/></svg>`},Do=(e,t)=>`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${e}"><rect width="${e}" height="${e}" fill="${_o(t)}"/></svg>`;var Ve=async e=>{let{size:t,paddingPercent:r,bg:n,fit:o,shape:s,radiusPercent:i,quality:a,forceSquare:c,sourceBuffer:l}=e,p=Math.max(1,Math.round(t*(1-r*2/100))),u=o==="cover"?"cover":"contain",f=await ut(l,{density:384}).resize(p,p,{fit:u,background:{r:0,g:0,b:0,alpha:0}}).png().toBuffer(),d=Math.round((t-p)/2),g=n==="transparent"?{r:0,g:0,b:0,alpha:0}:await Dr(n),y=await ut({create:{width:t,height:t,channels:4,background:g}}).composite([{input:f,left:d,top:d}]).png().toBuffer();if(!c&&(s!=="square"||o==="clip")){let x=s==="square"?"rounded":s,E=Buffer.from(_r(x,t,i));y=await ut(y).composite([{input:E,blend:"dest-in"}]).png({compressionLevel:Lo(a)}).toBuffer()}else y=await ut(y).png({compressionLevel:Lo(a)}).toBuffer();return y},Dr=async e=>{let t=await ut(Buffer.from(Do(1,e))).raw().toBuffer({resolveWithObject:!0}),[r,n,o,s=255]=t.data;return{r,g:n,b:o,alpha:s/255}},Lo=e=>{let t=Math.round((100-e)/100*9);return Math.max(0,Math.min(9,t))},Uo=async e=>{if(e.length===0)throw new Error("No frames provided to encodeIco");return kc(e)};import{readFile as Pc,writeFile as Xf}from"node:fs/promises";var Go=e=>e.map(({src:t,size:r})=>({src:t,sizes:`${r}x${r}`,type:"image/png",purpose:"any maskable"})),Bo=async(e,t)=>{let r=await Pc(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 Vo=e=>`icons: [
624
+ ${e.map(r=>` { src: '${r.src}', sizes: '${r.sizes}', type: '${r.type}'${r.purpose?`, purpose: '${r.purpose}'`:""} }`).join(`,
625
+ `)},
626
+ ],`,Ho=(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 Sc}from"node:fs/promises";import Lr from"node:path";var qo=[".png",".jpg",".jpeg",".webp",".svg",".avif"],Gt=async e=>{if(!e)throw new Error("Source image path is required (use --path).");let t=Lr.isAbsolute(e)?e:Lr.resolve(process.cwd(),e),r;try{r=await Sc(t)}catch{throw new Error(`Source image not found: ${t}`)}if(!r.isFile())throw new Error(`Source is not a file: ${t}`);let n=Lr.extname(t).toLowerCase();if(!qo.includes(n))throw new Error(`Unsupported source format '${n}'. Use one of: ${qo.join(", ")}`);return{absolutePath:t,ext:n,bytes:r.size,isVector:n===".svg"}};var Ur=(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 Cc=async e=>{try{return await Tc(e),!0}catch{return!1}},Oc=async(e,t,r)=>{let n=[];for(let o of e){if(await Cc(o.outPath)&&!t&&!o.alwaysWrite){n.push({path:o.outPath,bytes:o.bytes.length,skipped:!0});continue}r||(await Ec(Y.dirname(o.outPath),{recursive:!0}),await Rc(o.outPath,o.bytes)),n.push({path:o.outPath,bytes:o.bytes.length})}return n},Gr=async(e,t)=>{let r=await Gt(t.source),n=await Jo(r.absolutePath),o=t.ogSource?await Jo((await Gt(t.ogSource)).absolutePath):n,s=[],i=[];for(let p of t.icoSizes)i.push(await Ve({sourceBuffer:n,size:p,shape:"square",radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality,forceSquare:!0}));let a=await Uo(i);if(s.push({outPath:Y.join(t.outDir,"favicon.ico"),bytes:a}),t.emitIcon){let p=await Ve({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:Y.join(t.outDir,"icon.png"),bytes:p})}if(t.emitApple){let p=t.bg==="transparent"?"#ffffff":t.bg,u=await Ve({sourceBuffer:n,size:180,shape:t.shape,radiusPercent:t.radius,bg:p,paddingPercent:t.padding,fit:t.fit,quality:t.quality});s.push({outPath:Y.join(t.outDir,"apple-icon.png"),bytes:u})}if(t.emitOg){let p=await zo(o,t,!1);s.push({outPath:Y.join(t.outDir,"opengraph-image.png"),bytes:p})}if(t.emitTwitter){let p=await zo(o,t,!0);s.push({outPath:Y.join(t.outDir,"twitter-image.png"),bytes:p})}let c;if(t.pwa||t.pwaInit){let p=await Mr(e);if(t.pwa&&!p.isPwa)throw new Error("No PWA setup detected. Use --pwa-init to bootstrap a manifest, or set up next-pwa/serwist first.");let u=Y.join(e,"public"),f=[192,512],d=[];for(let y of f){let S=await Ve({sourceBuffer:n,size:y,shape:t.shape,radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality}),x=`icon-${y}.png`;s.push({outPath:Y.join(u,x),bytes:S}),d.push({src:`/${x}`,size:y})}let g=Go(d);if(p.publicManifestPath){let y=await Bo(p.publicManifestPath,g);s.push({outPath:p.publicManifestPath,bytes:Buffer.from(y,"utf-8"),alwaysWrite:!0})}else if(p.appManifestPath)c=Vo(g);else if(t.pwaInit){let y=Y.join(u,"manifest.webmanifest"),S=Ho(Y.basename(e)||"App","#000000",g);s.push({outPath:y,bytes:Buffer.from(S,"utf-8")})}}return{outputs:await Oc(s,t.force,t.dryRun),pwaSnippet:c}},zo=async(e,t,r)=>{let o=r?600:630,s=Math.round(Math.min(1200,o)*(1-t.padding*2/100)),i=await Ve({sourceBuffer:e,size:s,shape:t.shape,radiusPercent:t.radius,bg:"transparent",paddingPercent:0,fit:t.fit,quality:t.quality}),a=t.bg==="transparent"?"#ffffff":t.bg,c=await Dr(a),l=Math.round((1200-s)/2),p=Math.round((o-s)/2);return jc({create:{width:1200,height:o,channels:4,background:c}}).composite([{input:i,left:l,top:p}]).png().toBuffer()};var Wo=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 Ac=["square","rounded","circle","squircle"],Ic=["contain","cover","clip"],Ko=(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},Br=(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},Yo=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(je.cyan(`
628
+ \u{1F3A8} Favicon Generator
629
+ `));let n=await Fo({source:t.path}),o=await Nc(r,t.out),s=Ko(t.shape,Ac,"square","shape"),i=Ko(t.fit,Ic,"contain","fit"),a=Br(t.radius,20,"radius",0,50),c=Br(t.padding,0,"padding",0,30),l=Br(t.quality,90,"quality",1,100),p=Ur(t.sizes,[16,32,48]),u=t.bg??"transparent";_c(u);let f=t.all===!0,d={source:n.source,outDir:o,force:!!t.force,dryRun:!!t.dryRun,emitIcon:!!t.icon||f,emitApple:!!t.apple||f,emitOg:!!t.og||f,emitTwitter:!!t.twitter,ogSource:t.ogSource,pwa:!!t.pwa,pwaInit:!!t.pwaInit,shape:s,radius:a,bg:u,padding:c,fit:i,icoSizes:p,quality:l};h.start("Generating favicon assets...");let g=await Gr(r,d);h.succeed(d.dryRun?"Favicon plan ready":"Favicon assets generated"),m("");for(let y of g.outputs){let S=Bt.relative(r,y.path);y.skipped?m(je.yellow(` \u21B7 skipped ${S} (exists; use --force to overwrite)`)):d.dryRun?m(je.dim(` \u{1F4C4} would write ${S} (${Xo(y.bytes)})`)):m(je.green(` \u2713 ${S} `)+je.dim(`(${Xo(y.bytes)})`))}g.pwaSnippet&&(m(""),m(je.cyan("Add this to your typed manifest (src/app/manifest.ts):")),m(je.dim(g.pwaSnippet))),m("")}catch(r){h.fail("Favicon generation failed");let n=r instanceof Error?r.message:String(r);b(n),process.exit(1)}})},Nc=async(e,t)=>{if(t)return Bt.isAbsolute(t)?t:Bt.resolve(e,t);let r=await Ut(e);return r||(hn("Could not detect app/ or src/app/ \u2014 falling back to src/app"),Bt.join(e,"src","app"))},Fc=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,Mc=/^(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/]+\)$/i,_c=e=>{if(e!=="transparent"&&!Fc.test(e)&&!Mc.test(e)&&!Wo.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().`)},Xo=e=>e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`;import Vr from"node:path";import D from"picocolors";import Dc from"enquirer";var{prompt:Lc}=Dc,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 Lc(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 es=e=>{e.command("feature [name]").description("Generate a feature module (api/, components/, store/, index.ts, server.ts)").option("--api","Generate the api/ layer: schema, DAL, Server Actions, queries").option("--no-api","Skip the api/ layer").option("--store","Generate a client-state slice (Redux or Zustand, whichever the project uses)").option("--no-store","Skip the slice").option("--persist","Persist the Redux slice across reloads").option("--no-persist","Do not persist the slice").option("--path <path>","Base directory (default: src/features)").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(D.cyan(`
630
+ \u{1F3AF} Feature Generator
631
+ `)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected"),m(D.dim(` state: ${s.state} i18n: ${s.hasI18n?"\u2713":"\u2717"} tests: ${s.hasTests?"\u2713":"\u2717"}
632
+ `));let i=await Qo(t,s.state,{api:r.api,store:r.store,persist:r.persist});M(i.featureName,"feature name"),r.path&&W(r.path,"path");let a=r.path||Vr.join("src","features"),c=ne(n,a,i.featureName);await ke(n,i.featureName,a)&&(b(`Feature '${i.featureName}' already exists at ${a}.`),process.exit(1)),h.start("Generating feature files..."),await wr({name:i.featureName,outputPath:c,createStore:i.createStore,persistStore:i.persistStore,createApi:i.createApi,state:s.state,hasI18n:s.hasI18n,hasTests:s.hasTests}),h.succeed("Feature files generated"),s.hasI18n&&await Te(n,$(i.featureName)),i.createApi&&(h.start("Registering API endpoints..."),await lt({serviceName:i.featureName,projectPath:n}),h.succeed("API endpoints registered")),i.createStore&&s.state==="redux"&&(h.start("Registering slice in the store..."),await mt({projectPath:n,name:i.featureName,persist:i.persistStore,importPath:Vr.join(a,i.featureName,"store")}),h.succeed("Slice registered")),await k(n,o),Uc(i,a,s.state)}catch(n){h.fail("Feature generation failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})},Uc=(e,t,r)=>{let n=Vr.join(t,e.featureName);m(D.green(`
633
+ \u2728 Feature '${e.featureName}' created.
634
+ `)),m(D.dim(` ${n}/`)),e.createApi&&m(D.dim(" api/ schema, keys, server (DAL), actions, queries")),m(D.dim(" components/")),e.createApi||m(D.dim(" hooks/")),e.createStore&&m(D.dim(" store/")),m(D.dim(" types/")),m(D.dim(" index.ts client-safe barrel")),e.createApi&&m(D.dim(" server.ts server-only barrel")),m(""),m(D.cyan("Next:")),e.createApi&&(m(D.dim(` 1. Adjust the contracts in ${n}/api/schema.ts and the paths in src/lib/config/app-apis.ts`)),m(D.dim(` 2. Read in a Server Component: import { list${Zo(e.featureName)}s } from '@/features/${e.featureName}/server'`)),m(D.dim(` 3. Prefetch + hydrate for the client list: prefetchQuery(${Gc(e.featureName)}ListQuery())`))),e.createStore&&r==="zustand"&&m(D.yellow(` ! Compose create${Zo(e.featureName)}Slice into AppState in src/store/index.ts`)),m("")},Gc=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),Zo=$;import Hr from"node:path";import He from"picocolors";import Bc from"enquirer";var{prompt:Vc}=Bc,ts=async e=>e?{hookName:e}:await Vc({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 rs=e=>{e.command("hook [name]").description("Generate a custom React hook").option("--client","Add 'use client' directive (default: true)",!0).option("--feature <path>","Generate in feature directory (e.g., src/features/auth)").option("--test","Also generate a sibling test file").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(He.cyan(`
635
+ \u{1FA9D} Hook Generator
636
+ `));let s=await O(n),i=await ts(t);M(i.hookName,"hook name"),r.feature&&W(r.feature,"feature path");let c=`use${$(i.hookName)}`,l=Hc(r,s.hasTests);h.start("Generating hook..."),await br({name:i.hookName,projectPath:n,isClient:r.client??!0,featurePath:r.feature}),h.succeed("Hook generated");let p=r.feature?Hr.join(n,r.feature,"hooks",`${c}.ts`):Hr.join(n,"src/hooks",`${c}.ts`),u=null;l&&(h.start("Generating test..."),u=await oe({projectPath:n,sourceFile:p,kind:"hook",symbolName:c,hookUsesStore:s.state!=="none"&&!!r.feature}),h.succeed("Test generated"));let f=r.feature?`${r.feature}/hooks/${c}.ts`:`src/hooks/${c}.ts`;await k(n,o),m(He.green(`
637
+ \u2728 Hook '${c}' created successfully!
638
+ `)),m(He.dim(` \u{1F4C4} ${f}`)),u&&m(He.dim(` \u{1F9EA} ${Hr.relative(n,u)}`)),m(""),m(He.cyan("Usage:"));let d=r.feature?`${r.feature.replace(/^src\//,"@/")}/hooks/${c}`:`@/hooks/${c}`;m(He.dim(` import { ${c} } from '${d}';`)),m("")}catch(n){h.fail("Hook generation failed"),b(`${n}`),process.exit(1)}})},Hc=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import Vt from"picocolors";var ns=e=>{e.command("layout <segment>").description("Generate a nested layout.tsx (locale-aware when i18n is detected)").option("--group","Treat the segment as a route group (wraps it in parens)").option("--at <path>","Place the layout under this nested path (kebab-case, slash-separated)").option("--no-locale","Skip the locale wrapper even if i18n is detected").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(Vt.cyan(`
639
+ \u{1F4D0} Layout Generator
640
+ `)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected");let i=s.hasI18n&&r.locale!==!1;m(Vt.dim(` i18n: ${i?"\u2713":"\u2717"}
641
+ `)),h.start("Generating layout...");let a=await vr({segment:t,at:r.at,projectPath:n,hasI18n:i,withGroup:!!r.group});h.succeed("Layout generated"),await k(n,o),m(Vt.green(`
642
+ \u2728 ${a.componentName} created!
643
+ `)),m(Vt.dim(` \u{1F4C4} ${a.displayPath}`)),m("")}catch(n){h.fail("Layout generation failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Q from"picocolors";import qc from"enquirer";var{prompt:Jc}=qc,os=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 ss=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(),o=Date.now();m(Q.cyan(`
644
+ \u{1F30D} Locale Generator
645
+ `)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected"),s.hasI18n||(h.fail("i18n is not set up in this project"),b("Please set up internationalization first"),m(Q.dim(`
646
+ Run: npx @teispace/next-maker setup --i18n
647
+ `)),process.exit(1));let i=await os(t,{copyTranslations:r.copyTranslations,name:r.name,country:r.country,flag:r.flag});M(i.code,"locale code");let a=ne(n,"src","i18n","translations",`${i.code}.json`);w(a)&&(b(`Locale '${i.code}' already exists!`),process.exit(1)),h.start("Adding locale..."),await $r({...i,projectPath:n}),h.succeed("Locale added"),await k(n,o),m(Q.green(`
648
+ \u2728 Locale '${i.code}' (${i.name}) added successfully!
649
+ `)),m(Q.dim("Updated files:")),m(Q.dim(` \u{1F4C4} src/i18n/translations/${i.code}.json`)),m(Q.dim(" \u{1F4DD} src/types/i18n.ts (SupportedLocale type)")),m(Q.dim(" \u{1F4DD} src/lib/config/app-locales.ts")),m(""),m(Q.cyan("Next steps:")),m(Q.dim(` 1. Translate: src/i18n/translations/${i.code}.json`)),m(Q.dim(` 2. Visit: http://localhost:3000/${i.code}`)),m("")}catch(n){h.fail("Locale generation failed"),b(`${n}`),process.exit(1)}})};import zc from"node:path";import dt from"picocolors";var is=e=>{e.command("options").description("List the starter's options and this project's current values").option("--starter-path <dir>","Read the manifest from a local starter checkout").action(async t=>{let r;try{let n=process.cwd(),o=w(zc.join(n,".next-maker.json")),s=o?await re(n):{cli:"",starter:{name:"",version:"",source:""},packageManager:"pnpm",answers:{}},i=await De(s,{starterPath:t.starterPath});r=i.dispose,m(dt.cyan(`
650
+ ${i.manifest.starter.name} ${i.manifest.starter.version}
651
+ `));for(let[a,c]of Object.entries(i.manifest.options)){let l=o?s.answers[a]:c.default,p=c.type==="boolean"?"true | false":(c.values??[]).join(" | ");m(` ${dt.bold(a.padEnd(18))} ${String(Array.isArray(l)?l.join(",")||"none":l).padEnd(10)} ${dt.dim(p)}`),c.label&&m(` ${"".padEnd(18)} ${dt.dim(c.label)}`)}m(""),m(dt.dim(o?"Change with: next-maker setup --set <option>=<value>":"Use with: next-maker init --set <option>=<value>")),m("")}catch(n){b(`${n instanceof Error?n.message:n}`),process.exit(1)}finally{await r?.()}})};import qr from"node:path";import H from"picocolors";import Wc from"enquirer";var{prompt:Kc}=Wc,as=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 Kc(r):{};return{pageName:e??n.pageName,withLoading:t.loading??n.withLoading??!0,withError:t.error??n.withError??!0}};var cs=e=>{e.command("page [name]").description("Generate a new page/route").option("--dynamic <param>","Create dynamic route with parameter (e.g., id)").option("--group <name>","Route group to place the page in, e.g. app or marketing").option("--loading","Generate loading.tsx").option("--no-loading","Skip loading.tsx").option("--error","Generate error.tsx").option("--no-error","Skip error.tsx").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(H.cyan(`
652
+ \u{1F4C4} Page Generator
653
+ `)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected"),m(H.dim(` i18n: ${s.hasI18n?"\u2713":"\u2717"}
654
+ `));let i=await as(t,{loading:r.loading,error:r.error}),a=i.pageName;M(a,"page name"),r.dynamic&&M(r.dynamic,"dynamic param"),r.group&&M(r.group,"route group");let c=$(a),l=qr.join(s.hasI18n?"src/app/[locale]":"src/app",r.group?`(${r.group})`:"");await ke(n,a,l)&&(b(`Page '${a}' already exists at ${l}/${a}!`),process.exit(1)),h.start("Generating page files..."),await Pr({name:a,projectPath:n,hasI18n:s.hasI18n,group:r.group,dynamic:r.dynamic,withLoading:r.loading??i.withLoading,withError:r.error??i.withError}),h.succeed("Page files generated"),h.start("Registering route...");let u=r.dynamic?`/${a}/[${r.dynamic}]`:`/${a}`;await Cr(n,a,u),h.succeed("Route registered"),s.hasI18n&&(h.start("Adding translation namespace..."),await Te(n,c),h.succeed("Translation namespace added"));let f=r.dynamic?qr.join(l,a,`[${r.dynamic}]`):qr.join(l,a);await k(n,o),m(H.green(`
655
+ \u2728 Page '${a}' created successfully!
656
+ `)),m(H.dim("Generated files:")),m(H.dim(` \u{1F4C2} ${f}/`)),m(H.dim(" \u251C\u2500\u2500 page.tsx")),(r.loading??i.withLoading)&&m(H.dim(" \u251C\u2500\u2500 loading.tsx")),(r.error??i.withError)&&m(H.dim(" \u2514\u2500\u2500 error.tsx")),m(""),s.hasI18n&&(m(H.cyan("Next steps:")),m(H.dim(` 1. Add translations in: src/i18n/translations/en.json \u2192 "${c}"`)),m(H.dim(` 2. Visit: http://localhost:3000/${a}`)),m(""))}catch(n){h.fail("Page generation failed"),b(`${n}`),process.exit(1)}})};import Jr from"node:path";import fe from"picocolors";var ps=e=>{e.command("provider <name>").description("Generate a context provider in src/providers/ and wire it into RootProvider").action(async t=>{try{let r=process.cwd(),n=Date.now();m(fe.cyan(`
657
+ \u{1F50C} Provider Generator
658
+ `)),h.start("Generating provider...");let{componentName:o,fileBaseName:s,providerFile:i}=await Er({name:t,projectPath:r});h.succeed("Provider generated"),h.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:a,barrelFile:c}=await Nr({projectPath:r,componentName:o,fileBaseName:s});h.succeed("Provider registered"),await k(r,n),m(fe.green(`
659
+ \u2728 ${o} created!
660
+ `)),m(fe.dim("Generated files:")),m(fe.dim(` \u{1F4C4} ${Jr.relative(r,i)}`)),a?m(fe.dim(` \u270F\uFE0F ${Jr.relative(r,a)} \u2014 wrapped {children}`)):(m(fe.yellow(`
661
+ \u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),m(fe.dim(` <${o}>{children}</${o}>`))),c&&m(fe.dim(` \u270F\uFE0F ${Jr.relative(r,c)} \u2014 added re-export`)),m("")}catch(r){h.fail("Provider generation failed"),b(`${r instanceof Error?r.message:r}`),process.exit(1)}})};import Xc from"picocolors";var ls=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){b(`${n instanceof Error?n.message:n}`),process.exit(1)}}).addHelpText("after",`
662
+ ${Xc.dim("Example: next-maker remove ws")}
663
+ `)};import zr from"node:path";import Wr from"picocolors";var ms=e=>{e.command("api [name]").alias("service").description("Generate api/{schema,keys,server,queries,actions}.ts for a resource inside a feature").option("--feature <path>","Feature directory (default: src/features/<name>)").option("--no-actions","Skip actions.ts (read-only resource)").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();if(m(Wr.cyan(`
664
+ \u{1F50C} API Layer Generator
665
+ `)),!t)throw new Error("Pass the resource name, e.g. `next-maker api invoice`.");M(t,"resource name"),r.feature&&W(r.feature,"feature path");let s=await O(n);if(!s.hasQuery||!s.hasActions)throw new Error("This generator needs @tanstack/react-query and next-safe-action (starter 2.x).");let i=r.feature?ne(n,r.feature):ne(n,"src","features",t);if(w(zr.join(i,"api","schema.ts")))throw new Error(`${zr.relative(n,i)}/api already exists.`);h.start("Generating api/ files...");let a=await st({name:t,featurePath:i,withActions:r.actions!==!1});h.succeed("api/ generated"),h.start("Registering API endpoints..."),await lt({serviceName:t,projectPath:n}),h.succeed("Endpoints registered in src/lib/config/app-apis.ts"),await k(n,o),m(Wr.green(`
666
+ \u2728 ${zr.relative(n,i)}/api ready.
667
+ `));for(let c of a)m(Wr.dim(` ${c}`));m("")}catch(n){h.fail("API generation failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import wp from"enquirer";import q from"picocolors";import{execFile as op}from"node:child_process";import{cp as ey,mkdir as ws,readFile as sp,rm as xs,stat as ty,writeFile as qt}from"node:fs/promises";import ee from"node:path";import{promisify as ip}from"node:util";var Kr=(e,t)=>{let r=e.length,n=t.length;if((r+1)*(n+1)>4e6)return null;let o=n+1,s=new Int32Array((r+1)*o);for(let u=r-1;u>=0;u--)for(let f=n-1;f>=0;f--)s[u*o+f]=e[u]===t[f]?s[(u+1)*o+f+1]+1:Math.max(s[(u+1)*o+f],s[u*o+f+1]);let i=[],a=null,c=0,l=0,p=()=>(a||(a={start:c,end:c,insert:[]},i.push(a)),a);for(;c<r||l<n;){let u=c*o+l;c<r&&l<n&&e[c]===t[l]&&s[u]===s[u+o+1]+1?(a=null,c++,l++):l<n&&(c>=r||s[u+1]>=s[u+o])?(p().insert.push(t[l]),l++):p().end=++c}return i},Z=e=>e.start===e.end,Yc=(e,t)=>Z(e)&&Z(t)?!1:Z(e)?t.start<e.start&&e.start<t.end:Z(t)?e.start<t.start&&t.start<e.end:Math.max(e.start,t.start)<Math.min(e.end,t.end),Qc=(e,t)=>e.start===t.start&&e.end===t.end&&e.insert.length===t.insert.length&&e.insert.every((r,n)=>r===t.insert[n]),us=(e,t,r)=>{let n=t.start,o=t.insert;for(;n<r.end&&o.length>0&&o[0]===e[n];)o=[...o.slice(1),o[0]],n++;if(n===r.end)return{start:n,end:n,insert:o};for(n=t.start,o=t.insert;n>r.start&&o.length>0&&o[o.length-1]===e[n-1];)o=[o[o.length-1],...o.slice(0,-1)],n--;return n===r.start?{start:n,end:n,insert:o}:null},Xr=(e,t)=>e.map(r=>({start:r.start+t,end:r.end+t,insert:r.insert})),ds=(e,t,r,n)=>{let o=[],s=0,i=0,a=0,c=p=>{for(let u=s;u<p.start;u++)o.push(e[u]);o.push(...p.insert),s=Math.max(s,p.end)},l=(p,u)=>{if(!n)return!1;let f=Math.min(p.start,u.start),d=Math.max(p.end,u.end),g=i,y=a,S=!0;for(;S;){for(S=!1;g+1<t.length&&t[g+1].start<d;)g++,d=Math.max(d,t[g].end),S=!0;for(;y+1<r.length&&r[y+1].start<d;)y++,d=Math.max(d,r[y].end),S=!0}let x=n(e.slice(f,d),Xr(t.slice(i,g+1),-f),Xr(r.slice(a,y+1),-f));return x?(c({start:f,end:d,insert:x}),i=g+1,a=y+1,!0):!1};for(;i<t.length||a<r.length;){let p=t[i],u=r[a];if(p&&u){if(Qc(p,u)){c(p),i++,a++;continue}if(Yc(p,u)){let d=Z(p)&&!Z(u)?us(e,p,u):Z(u)&&!Z(p)?us(e,u,p):null;if(d&&Z(p))p=d;else if(d)u=d;else{if(l(p,u))continue;return null}}p.start<u.start||p.start===u.start&&Z(p)?(c(p),i++):(c(u),a++)}else p?(c(p),i++):u&&(c(u),a++)}for(let p=s;p<e.length;p++)o.push(e[p]);return o},Zc=(e,t,r,n)=>{let o=Kr(e,t),s=Kr(e,r);return o&&s?ds(e,o,s,n):null},ep=/\s+|[\p{L}\p{N}_$]+\s*|[^\s\p{L}\p{N}_$]\s*/gu,fs=e=>e.match(ep)??[],tp=e=>e.flatMap(t=>fs(`${t}
668
+ `)),ft=e=>{if(e==="")return[];let t=e.split(`
669
+ `);return t[t.length-1]===""&&t.pop(),t},Ht=e=>e.length?`${e.join(`
670
+ `)}
671
+ `:"",rp=(e,t,r)=>{let n=[],o=[];for(let l of e)o.push(n.length),n.push(...fs(`${l}
672
+ `));o.push(n.length);let s=l=>{let p=[];for(let u of l){let f=o[u.start],d=Kr(n.slice(f,o[u.end]),tp(u.insert));if(!d)return null;p.push(...Xr(d,f))}return p},i=s(t),a=s(r);if(!i||!a)return null;let c=ds(n,i,a);return c?ft(c.join("")):null},np=(e,t,r)=>{let n=Zc(ft(t),ft(e),ft(r),rp);return n?Ht(n):null},gs=(e,t)=>{let r=e.split(`
673
+ `),n=e.endsWith(`
674
+ `);n&&r.pop();let o=[],s=0,i=0;for(;i<r.length;){let a=r[i];if(!a.startsWith("<<<<<<< ")){o.push(a),i++;continue}let c=[],l=[],p=[],u=c;i++;let f=!1;for(;i<r.length;){let g=r[i++];if(u===c&&g.startsWith("||||||| "))u=l;else if((u===c||u===l)&&g==="=======")u=p;else if(u===p&&g.startsWith(">>>>>>> ")){f=!0;break}else u.push(g)}if(!f)throw new Error("Malformed conflict block in merge output.");let d=np(Ht(c),Ht(l),Ht(p));if(d!==null){o.push(...ft(d));continue}s++,o.push(`<<<<<<< ${t.ours}`,...c,"=======",...p,`>>>>>>> ${t.theirs}`)}return{content:o.join(`
675
+ `)+(n?`
676
+ `:""),conflicts:s}};var ap=ip(op),cp=new Set(["pnpm-lock.yaml","package-lock.json","yarn.lock","bun.lock","bun.lockb",".env",".next-maker.json","next-env.d.ts","tsconfig.tsbuildinfo"]),pp=e=>cp.has(e)||e.startsWith(".env.")||e.endsWith(".log"),lp=new Set([".png",".jpg",".jpeg",".gif",".webp",".avif",".ico",".woff",".woff2",".ttf",".otf",".pdf",".zip",".gz"]),Yr=(e,t)=>lp.has(ee.extname(e))||t.subarray(0,8e3).includes(0),Qr=async e=>{try{return await sp(e)}catch{return null}},gt=async(e,t,r)=>{r||(await ws(ee.dirname(e),{recursive:!0}),await qt(e,t))},hs=async(e,t,r,n={ours:"project",base:"starter (previous)",theirs:"starter (new)"})=>{let o=ee.join(process.env.TMPDIR??"/tmp",`next-maker-merge-${process.pid}-${Math.random().toString(36).slice(2)}`);await ws(o,{recursive:!0});try{await qt(ee.join(o,"ours"),e),await qt(ee.join(o,"base"),t),await qt(ee.join(o,"theirs"),r);try{let{stdout:s}=await ap("git",["merge-file","-p","--diff3","-L",n.ours,"-L",n.base,"-L",n.theirs,"ours","base","theirs"],{cwd:o,maxBuffer:67108864});return{content:s,conflicts:0}}catch(s){let i=s;if(typeof i.code=="number"&&i.code>0&&i.code<128&&typeof i.stdout=="string")return gs(i.stdout,n);throw i.code==="ENOENT"?new Error("git is required for merges but was not found on PATH."):s}}finally{await xs(o,{recursive:!0,force:!0})}},ys=["scripts","dependencies","devDependencies","peerDependencies","optionalDependencies","engines"],mp=(e,t,r)=>{let n=structuredClone(e),o=[],s=(a,c)=>JSON.stringify(a)===JSON.stringify(c),i=(a,c,l,p,u,f)=>{let d=l[u],g=p[u],y=c[u];if(!s(d,g)){if(s(y,d)||s(y,g)){g===void 0?delete a[u]:a[u]=g;return}o.push(f)}};for(let a of ys){let c=t[a]??{},l=r[a]??{},p=e[a]??{};n[a]||(n[a]={});let u=n[a];for(let f of new Set([...Object.keys(c),...Object.keys(l)]))i(u,p,c,l,f,`${a}.${f}`);Object.keys(u).length===0&&!(a in r)?delete n[a]:a!=="scripts"&&a!=="engines"&&(n[a]=Object.fromEntries(Object.entries(u).sort(([f],[d])=>f.localeCompare(d))))}for(let a of new Set([...Object.keys(t),...Object.keys(r)]))ys.includes(a)||i(n,e,t,r,a,a);return{result:n,conflicts:o}},qe=async({base:e,theirs:t,ours:r},n={})=>{let o=n.dryRun??!1,s=n.skip??pp,[i,a,c]=await Promise.all([L(e),L(t),L(r)]),l=new Set(c),p=new Set(i),u=new Set(a),f={entries:[],conflicts:[]},d=(g,y,S)=>{f.entries.push({file:g,outcome:y,note:S}),y==="conflict"&&f.conflicts.push(g)};for(let g of[...new Set([...i,...a])].sort()){if(s(g))continue;let y=p.has(g),S=u.has(g),x=l.has(g),[E,R,U]=await Promise.all([y?Qr(ee.join(e,g)):null,S?Qr(ee.join(t,g)):null,x?Qr(ee.join(r,g)):null]),Oe=ee.join(r,g);if(S&&!y){if(!x)await gt(Oe,R,o),d(g,"added");else if(U.equals(R))d(g,"unchanged");else if(Yr(g,R))d(g,"kept","starter added a binary the project also has");else{let ht=await hs(U.toString("utf8"),"",R.toString("utf8"));await gt(Oe,ht.content,o),d(g,ht.conflicts?"conflict":"merged")}continue}if(y&&!S){if(!x)continue;U.equals(E)?(o||await xs(Oe,{force:!0}),d(g,"deleted")):d(g,"kept","starter removed it, but the project changed it");continue}if(!x){d(g,"kept","deleted in the project");continue}if(E.equals(R)||U.equals(R)){d(g,"unchanged");continue}if(U.equals(E)){await gt(Oe,R,o),d(g,"updated");continue}if(Yr(g,U)||Yr(g,R)){d(g,"kept","binary changed on both sides");continue}if(g==="package.json"){let{result:ht,conflicts:yt}=mp(JSON.parse(U.toString("utf8")),JSON.parse(E.toString("utf8")),JSON.parse(R.toString("utf8")));await gt(Oe,`${JSON.stringify(ht,null,2)}
677
+ `,o),d(g,yt.length?"conflict":"merged",yt.length?`project kept: ${yt.join(", ")}`:void 0),yt.length&&f.conflicts.pop();continue}let nn=await hs(U.toString("utf8"),E.toString("utf8"),R.toString("utf8"));await gt(Oe,nn.content,o),d(g,nn.conflicts?"conflict":"merged")}return f};import{mkdtemp as up,readFile as dp,rm as bs}from"node:fs/promises";import{tmpdir as fp}from"node:os";import ge from"node:path";var Jt=async(e,t)=>{if(t.identity)return me(t.identity.projectName,{...t.identity,copyEnv:!1});let r=JSON.parse(await dp(ge.join(e,"package.json"),"utf-8"));return me(ge.basename(e),{projectName:r.name??ge.basename(e),description:r.description,author:typeof r.author=="string"?r.author:void 0,version:r.version,readme:w(ge.join(e,"README.md")),copyEnv:!1})},vs=e=>{let t=e.starter.source;if(t.startsWith("/")||t.startsWith(".")){if(!w(t))throw new Error(`The starter this project was generated from is a local path that no longer exists (${t}). Pass --from <ref|dir>.`);return{kind:"local",location:ge.resolve(t)}}return{kind:"remote",location:t}},Zr=e=>e.startsWith("/")||e.startsWith(".")||w(e)?{kind:"local",location:ge.resolve(e)}:V({ref:e.includes("#")?e.split("#")[1]:e,starterPath:void 0}),Je=async(e,t,r,n,o)=>{let s=await up(ge.join(fp(),"next-maker-ref-")),i=ge.join(s,r.projectName);try{let a=await ve({projectPath:i,source:e,supplied:t,packageManager:n});return await $e(i,r,a,e),o&&await pr(i,["."],o),{dir:i,scaffold:a,dispose:()=>bs(s,{recursive:!0,force:!0})}}catch(a){throw await bs(s,{recursive:!0,force:!0}),a}};import gp from"enquirer";import T from"picocolors";var{prompt:hp}=gp,yp={added:T.green("+"),updated:T.cyan("\u2191"),merged:T.cyan("\u21C4"),deleted:T.red("-"),conflict:T.red("\u2717"),kept:T.yellow("\xB7"),skipped:T.dim("\xB7")},en=e=>{let t=e.entries.filter(n=>n.outcome!=="unchanged");if(t.length===0){m(T.dim(" no file changes"));return}for(let n of t)m(` ${yp[n.outcome]??" "} ${n.outcome.padEnd(8)} ${n.file}${n.note?T.dim(` (${n.note})`):""}`);let r={};for(let n of t)r[n.outcome]=(r[n.outcome]??0)+1;if(m(""),m(T.dim(` ${Object.entries(r).map(([n,o])=>`${o} ${n}`).join(", ")}`)),e.conflicts.length){m(""),m(T.red(` ${e.conflicts.length} file(s) need attention; look for <<<<<<< markers:`));for(let n of e.conflicts)m(T.red(` ${n}`))}},$s=e=>{e.command("upgrade").description(`Merge starter changes into this project (default target: ${Et})`).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 re(r),s=await Jt(r,o),i=t.from?Zr(t.from):vs(o),a=t.to?Zr(t.to):V({starterPath:t.starterPath,ref:Et});if(i.location===a.location){m(T.dim(`Already on ${a.location}; nothing to upgrade.`));return}m(T.cyan(`
678
+ \u2B06 Upgrade
679
+ `)),m(` ${T.dim("from")} ${i.location}`),m(` ${T.dim("to ")} ${a.location}
680
+ `);let c=await Je(i,o.answers,s,o.packageManager,r);n.push(c.dispose);let l=await Je(a,o.answers,s,o.packageManager,r);n.push(l.dispose);for(let d of l.scaffold.forced)m(T.yellow(` ! ${d.reason}; ${d.option} turned off`));for(let d of l.scaffold.unknown)m(T.yellow(` ! option "${d}" no longer exists in the new starter`));let p=await qe({base:c.dir,theirs:l.dir,ours:r},{dryRun:!0});if(en(p),t.dryRun){m(T.dim(`
681
+ Dry run: nothing was written.
682
+ `));return}if(!t.yes){let{confirm:d}=await hp({type:"confirm",name:"confirm",message:"Apply these changes?",initial:!1});if(!d){m(T.yellow(`Aborted.
683
+ `));return}}let u=Date.now(),f=await qe({base:c.dir,theirs:l.dir,ours:r});await k(r,u),await ue(r,{...o,cli:Ae.version,starter:{...l.scaffold.manifest.starter,source:a.location},answers:l.scaffold.answers,identity:o.identity??{projectName:s.projectName,description:s.description,author:s.author,version:s.version,email:s.email,gitRemote:s.gitRemote,readme:s.readme}}),t.install!==!1&&(m(T.dim(`
684
+ Installing with ${o.packageManager}...`)),await le(r,o.packageManager)),m(T.green(`
685
+ \u2713 Upgraded to ${l.scaffold.manifest.starter.version}.`)),f.conflicts.length?m(T.yellow(" Resolve the conflicts above, then run `next-maker doctor --compile`.")):m(T.dim(" Run `next-maker doctor --compile` to confirm.")),m("")}catch(o){b(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{for(let o of n)await o()}})};var{prompt:xp}=wp,bp=(e,t=[])=>[...t,e],ks=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",bp).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 re(r),s=xe(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 De(o,{starterPath:t.starterPath});n.push(i.dispose);let{answers:a,forced:c,unknown:l}=Ye(i.manifest,{...o.answers,...s});for(let x of l)m(q.yellow(` ! unknown option "${x}" ignored`));for(let x of c)m(q.yellow(` ! ${x.reason}; ${x.option} turned off`));let p=Object.keys(a).filter(x=>JSON.stringify(a[x])!==JSON.stringify(o.answers[x]));if(m(q.cyan(`
686
+ \u{1F527} Setup
687
+ `)),p.length===0){m(q.dim(`No option changes; the project already has these values.
688
+ `));return}for(let x of p)m(` ${q.dim(x.padEnd(18))} ${String(o.answers[x])} ${q.dim("\u2192")} ${String(a[x])}`);m("");let u=await Jt(r,o),f=await Je(i.source,o.answers,u,o.packageManager,r);n.push(f.dispose);let d=await Je(i.source,a,u,o.packageManager,r);n.push(d.dispose);let g=await qe({base:f.dir,theirs:d.dir,ours:r},{dryRun:!0});if(en(g),t.dryRun){m(q.dim(`
689
+ Dry run: nothing was written.
690
+ `));return}if(!t.yes){let{confirm:x}=await xp({type:"confirm",name:"confirm",message:"Apply these changes?",initial:!1});if(!x){m(q.yellow(`Aborted.
691
+ `));return}}let y=Date.now(),S=await qe({base:f.dir,theirs:d.dir,ours:r});await k(r,y),await ue(r,{...o,answers:a}),t.install!==!1&&(m(q.dim(`
692
+ Installing with ${o.packageManager}...`)),await le(r,o.packageManager)),m(q.green(`
693
+ \u2713 Applied.`)),S.conflicts.length&&m(q.yellow(" Resolve the conflicts above, then run `next-maker doctor --compile`.")),m("")}catch(o){b(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{for(let o of n)await o()}})};import tn from"node:path";import zt from"picocolors";import vp from"enquirer";var{prompt:$p}=vp,Ps=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 $p(r):{};return{sliceName:e||n.sliceName,persistSlice:t===void 0?n.persistSlice||!1:t}};var Ss=e=>{e.command("slice [name]").description("Generate a store slice (Redux or Zustand, whichever the project uses)").option("--path <path>","Directory for the slice (default: src/store/slices/<name>)").option("--persist","Persist the slice across reloads (Redux)").option("--no-persist","Do not persist the slice").option("--test","Also generate a sibling test file (Redux)").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(zt.cyan(`
694
+ \u{1F527} Slice Generator
695
+ `)),h.start("Detecting project setup...");let s=await O(n);if(h.succeed("Project setup detected"),s.state==="none")throw new Error("This project has no client store. Re-run init with a state store, or add one with `next-maker setup --set state=redux`.");m(zt.dim(` state: ${s.state}
696
+ `));let i=await Ps(t,s.state==="zustand"?!1:r.persist);M(i.sliceName,"slice name"),r.path&&W(r.path,"path");let a=r.path??tn.join("src","store","slices"),c=ne(n,a,i.sliceName);if(await ke(n,i.sliceName,a))throw new Error(`Slice '${i.sliceName}' already exists at ${a}.`);h.start("Generating slice files..."),await it({name:i.sliceName,outputPath:c,persist:i.persistSlice,state:s.state}),h.succeed("Slice files generated"),s.state==="redux"&&(h.start("Registering slice in the store..."),await mt({projectPath:n,name:i.sliceName,persist:i.persistSlice,importPath:tn.join(a,i.sliceName)}),h.succeed("Slice registered"),(r.test??s.hasTests)&&(h.start("Generating test..."),await oe({projectPath:n,sourceFile:tn.join(c,`${i.sliceName}.slice.ts`),kind:"slice",symbolName:i.sliceName.replace(/-([a-z])/g,(l,p)=>p.toUpperCase())}),h.succeed("Test generated"))),await k(n,o),m(zt.green(`
697
+ \u2728 Slice '${i.sliceName}' created in ${a}/${i.sliceName}/.
698
+ `)),s.state==="zustand"&&(m(zt.yellow(` ! Compose create${$(i.sliceName)}Slice into AppState in src/store/index.ts`)),m(""))}catch(n){h.fail("Slice generation failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import J from"node:path";import rn from"picocolors";var Es=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
699
+ Infers the kind from filename/content; pass --kind to override.`).option("--kind <kind>","Override inferred kind: component | hook | slice").option("--force","Overwrite an existing test file").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now(),s=J.isAbsolute(t)?t:J.join(n,t);m(rn.cyan(`
700
+ \u{1F9EA} Test Generator
701
+ `)),w(s)||(b(`Source file not found: ${t}`),process.exit(1));let i=await O(n);i.hasTests||(b("Testing is not installed in this project. Run `next-maker setup --tests` first."),process.exit(1));let a=await j(s),c=kp(r.kind)??Pp(s,a);c||(b(`Could not infer test kind for ${J.basename(s)}. Pass --kind component|hook|slice.`),process.exit(1));let l=Sp(s,c),p=Ep(s,c);w(p)&&!r.force&&(b(`Test file already exists: ${J.relative(n,p)}. Use --force to overwrite.`),process.exit(1)),h.start(`Generating ${c} test...`);let u=await oe({projectPath:n,sourceFile:s,kind:c,symbolName:l,hasState:i.state!=="none",hasI18n:i.hasI18n,hookUsesStore:c==="hook"&&i.state!=="none"&&a.includes("useAppSelector")});h.succeed("Test generated"),await k(n,o),m(rn.green(`
702
+ \u2728 Test '${J.basename(u)}' created.
703
+ `)),m(rn.dim(` \u{1F9EA} ${J.relative(n,u)}`)),m("")}catch(n){h.fail("Test generation failed"),b(`${n}`),process.exit(1)}})},kp=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},Pp=(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},Sp=(e,t)=>{let r=J.basename(e).replace(/\.(tsx?|jsx?)$/i,"");return t==="slice"?G(r.replace(/\.slice$/,"")):t==="hook"?/^use[A-Z]/.test(r)?r:`use${$(r.replace(/^use-?/,""))}`:$(r)},Ep=(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 Tp,cp as Rp,mkdir as Wt,readFile as Kt,rm as jp,writeFile as N}from"node:fs/promises";import v from"node:path";import he from"picocolors";var Ts=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)}
704
+ `},Rs=e=>`packages:
705
+ - 'apps/*'
706
+ - 'packages/*'
707
+
708
+ ${e.split(`
709
+ `).filter(r=>!r.startsWith("packages:")&&!r.startsWith("# Single-package")&&!r.startsWith("# Monorepo mode")).join(`
868
710
  `).trim()}
869
- `,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"}},Fo=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:
870
- ${r.map(i=>`- ${i}`).join(`
871
- `)}`)};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 il(i,n);if(o.type==="cancel"){console.log(ci.yellow("Setup cancelled."));return}if(t.start("Setting up HTTP client(s)..."),await Fo(e),await No(r,t),o.type==="replace"&&o.fromClient&&(t.text="Migrating client usages...",await jr(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 Ao(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(),Xi));for(let k of o.clients){let Z=ir.join(e,k==="fetch"?I.FETCH_CLIENT:I.AXIOS_CLIENT);await v(Z)}Oo(i,o).length===0&&(t.text="Performing full cleanup...",await _o(e),await Mr(e))}let s=Oo(i,o);s.length>0?(t.text="Configuring client...",await Ar(e,s),await _r(e),await Fr(e),await Or(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 rl.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(ci.green(nl(o)))}catch(i){throw t.fail("Failed to setup HTTP Client."),i}finally{await v(r)}};async function il(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(ci.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 Oo(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 nl(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 ol from"node:fs/promises";import Ae from"node:path";import sl from"picocolors";w();Be();var Do=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 ol.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(sl.green(d.length>0?`HTTP Client repaired (${d.join("; ")}).`:"HTTP Client repaired."))}catch(n){throw r.fail("Failed to repair HTTP Client."),n}};var al=/<HttpClientBundleSentinel\s*\/>/,jo={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:al,removePattern:_t}],apply:O(or,Do)};P();import pi from"node:path";import ll from"picocolors";w();P();import cl from"node:fs/promises";import Te from"node:path";w();var Lo=async(e,t)=>{t.text="Fetching assets from starter repo...",await J(e)},Mo=async(e,t)=>{let r=Te.join(t,"src/i18n"),i=Te.join(e,a.I18N_DIR);await cl.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 Go=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:""}},Ho=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 pl 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';
872
- ${r}`;let i=/export default (.*?);/;i.test(r)?r=r.replace(i,`
873
- const withNextIntl = createNextIntlPlugin();
874
- export default withNextIntl($1);`):(r+=`
875
- const withNextIntl = createNextIntlPlugin();
876
- `,r+=`export default withNextIntl(nextConfig);
877
- `),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';
878
- `,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';
879
- `,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';
880
- import type { SupportedLocale } from '@/types/i18n';
881
- `+r),n&&(r=`${i}
882
- ${r}`),!r.includes("locale: SupportedLocale")){let o=` locale: SupportedLocale;
883
- messages: AbstractIntlMessages;
884
- 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}
885
- ${o}
886
- ${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=`
887
- ${u?`${u},
888
- `:""}locale,
889
- messages,
890
- timeZone,`,x=`
891
- ${d?`${d};
892
- `:""}locale: SupportedLocale;
893
- messages: AbstractIntlMessages;
894
- timeZone: string;`;r=r.replace(p[0],`export const RootProvider = ({${f}
895
- }: {${x}
896
- }) => {`)}}}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}>
897
- {children}
898
- </NextIntlClientProvider>`;if(s.includes("{children}")){let p=s.replace("{children}",c);r=r.replace(o[0],`return (
899
- ${p}
900
- );`)}else r=r.replace(o[0],`return (
901
- <NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
902
- ${s}
903
- </NextIntlClientProvider>
904
- );`)}}await h(t,r)}},Uo=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 pl.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';
905
- import { hasLocale } from 'next-intl';
906
- import { notFound } from 'next/navigation';
907
- import { getMessages, getTimeZone, setRequestLocale } from 'next-intl/server';
908
- import { getLocaleDirection } from '@/lib/config/app-locales';
909
- ${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)+`
910
-
911
- export function generateStaticParams() {
912
- return routing.locales.map((locale) => ({ locale }));
913
- }`+o.slice(u)}}o=o.replace(/Readonly<\{\s*children:\s*React\.ReactNode;\s*}>/,`Readonly<{
914
- children: React.ReactNode;
915
- params: Promise<{ locale: string }>;
916
- }>`);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)+`
917
- const { locale } = await params;
918
-
919
- if (!hasLocale(routing.locales, locale)) {
920
- notFound();
921
- }
922
-
923
- setRequestLocale(locale);
711
+ `,js=()=>`${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)}
712
+ `,Cs=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)}
713
+ `,Os=()=>`# dependencies
714
+ node_modules
715
+ .pnpm-store
716
+
717
+ # turborepo
718
+ .turbo
719
+
720
+ # build and test output
721
+ .next
722
+ out
723
+ coverage
724
+ test-results
725
+ playwright-report
726
+ .vitest
727
+ *.tsbuildinfo
728
+ next-env.d.ts
729
+
730
+ # env
731
+ .env
732
+ .env.*.local
733
+
734
+ # misc
735
+ .DS_Store
736
+ *.pem
737
+ npm-debug.log*
738
+ .pnpm-debug.log*
739
+ `,As=(e,t)=>`# ${e.name}
740
+
741
+ ${e.description}
742
+
743
+ 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/\`.
924
744
 
925
- const [messages, timeZone] = await Promise.all([getMessages(), getTimeZone()]);
926
- `+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';
927
- import { setRequestLocale } from 'next-intl/server';
928
- ${o}`),o=o.replace(/export default function Home\(\)/,`type Props = {
929
- params: Promise<{ locale: string }>;
930
- };
931
-
932
- export default async function Home(props: Props)`);let s=o.indexOf("export default async function Home(props: Props) {");if(s!==-1){let c=`
933
- const locale = (await props.params).locale as SupportedLocale;
934
- setRequestLocale(locale);
935
- `,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=pi.join(e,".next-maker-temp-i18n");try{let{isSetup:i,reason:n}=await Go(e);if(i){t.fail(`i18n is already set up (${n}).`);return}await Ho(e),await Lo(r,t),t.text="Copying i18n files...",await Mo(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 Uo(e),t.text="Installing dependencies...",await z(e,"next-intl");let o=pi.join(e,a.VITEST_CONFIG);if(l(o)){let c=JSON.parse(await g(pi.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(ll.green("Internationalization setup successfully!"))}catch(i){throw t.fail("Failed to setup Internationalization."),i}finally{await v(r)}};P();import ml from"node:fs/promises";import Pt from"node:path";import Bo from"picocolors";w();var dl="src/providers/RootProvider.tsx",zo=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 ml.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,dl)&&(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(Bo.green(f.length>0?`Internationalization repaired (${f.join("; ")}).`:"Internationalization repaired.")),d&&console.log(Bo.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 Vo={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:go},{file:"src/providers/RootProvider.tsx",description:"<NextIntlClientProvider> wrap",presence:/NextIntlClientProvider/,removePattern:Ve("NextIntlClientProvider")}],apply:O(mr,zo)};P();w();import yl from"node:path";P();import ul from"node:path";import fl from"picocolors";w();var Jo=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/,dr=e=>/reactCompiler\s*:/.test(e),vt=e=>{if(dr(e))return e;if(!Jo.test(e))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");return e.replace(Jo,`$1
936
- reactCompiler: true,`)};var ur=async e=>{let t=S("Enabling React Compiler...");try{let r=ul.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(fl.green("React Compiler enabled."))}catch(r){throw t.fail("Failed to enable React Compiler."),r}};P();import gl from"node:path";import hl from"picocolors";w();var Wo=async(e,t)=>{let r=S("Repairing React Compiler...");try{if(U(t,a.NEXT_CONFIG)){let n=gl.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(hl.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 qo={id:"react-compiler",name:"React Compiler",description:"reactCompiler: true in next.config.ts + babel-plugin-react-compiler",detect:async e=>{let t=yl.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,Wo)};P();import di from"node:path";import xl from"picocolors";w();P();import Xo from"node:fs/promises";import _e from"node:path";w();var Yo=async(e,t)=>{t.text="Fetching assets from starter repo...",await J(e)},Ko=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 Xo.cp(n,o,{recursive:!0})},Zo=async(e,t)=>{let r=_e.join(t,"src/features/counter"),i=_e.join(e,a.COUNTER_FEATURE);await Xo.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 li from"node:path";var Qo=async e=>{let t=li.join(e,a.STORE),r=li.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:""}},es=async e=>{let t=li.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 mi from"node:path";var ts=async e=>{},fr=async e=>{let t=mi.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';
937
- ${r}`),r.includes("from './StoreProvider'")||(r=`${r.includes("from '@/store'"),""}import { StoreProvider } from './StoreProvider';
938
- ${r}`),n&&(r=`${i}
939
- ${r}`),r.includes("preloadedState")||(r=r.replace(/children:\s*React\.ReactNode;/,`children: React.ReactNode;
940
- 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 (
941
- <StoreProvider preloadedState={preloadedState}>
942
- ${s}
943
- </StoreProvider>
944
- );`)}}await h(t,r)},rs=async e=>{let t=[mi.join(e,a.ROOT_PAGE),mi.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';
945
- ${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)+`
946
- <div className="mt-8">
947
- <h2 className="mb-4 text-2xl font-bold">Redux Counter</h2>
948
- <Counter />
949
- </div>
950
- `+i.slice(s),await h(r,i))}};var gr=async e=>{let t=S("Setting up Redux Toolkit..."),r=di.join(e,".next-maker-temp-redux");try{let{isSetup:i,reason:n}=await Qo(e);if(i){t.fail(`Redux is already set up (${n}).`);return}await es(e),await Yo(r,t),t.text="Copying Redux files...",await Ko(e,r),t.text="Creating Counter feature...",await Zo(e,r),t.text="Updating providers and pages...",await ts(e),await fr(e),await rs(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=di.join(e,a.VITEST_CONFIG);if(l(o)){let c=JSON.parse(await g(di.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(xl.green("Redux Toolkit setup successfully!"))}catch(i){throw t.fail("Failed to setup Redux Toolkit."),i}finally{await v(r)}};P();import wl from"node:fs/promises";import St from"node:path";import Pl from"picocolors";w();var vl="src/providers/RootProvider.tsx",is=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 wl.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,vl)&&(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(Pl.green(d.length>0?`Redux Toolkit repaired (${d.join("; ")}).`:"Redux Toolkit repaired."))}catch(n){throw r.fail("Failed to repair Redux Toolkit."),n}};var ns={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,is)};P();w();import Il from"node:path";P();import Tl from"node:path";import Cl from"picocolors";w();var ss=[{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"}],os=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/,Sl=/^\s+headers\s*:/m,El=e=>`
951
- headers: async () => {
952
- return [
953
- {
954
- source: '/:path*',
955
- headers: [
956
- ${e.map(({key:r,value:i})=>` { key: '${r}', value: '${i}' },`).join(`
745
+ \`\`\`
746
+ ${e.name}/
747
+ apps/
748
+ ${e.apps.map(r=>` ${r}/`).join(`
957
749
  `)}
958
- ],
959
- },
960
- ];
961
- },`,Je=e=>Sl.test(e),Et=(e,t=ss)=>{if(Je(e))return e;if(!os.test(e))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");return e.replace(os,`$1${El(t)}`)};var hr=async e=>{let t=S("Adding security headers to next.config.ts...");try{let r=Tl.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(Cl.green("Security headers added."))}catch(r){throw t.fail("Failed to add security headers."),r}};P();import Rl from"node:path";import bl from"picocolors";w();var as=async(e,t)=>{if(!U(t,a.NEXT_CONFIG))return;let r=S("Repairing security headers...");try{let i=Rl.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(bl.green("Security headers restored."))}catch(i){throw r.fail("Failed to repair security headers."),i}};var cs={id:"security-headers",name:"Security Headers",description:"Hardened HTTP headers in next.config.ts",detect:async e=>{let t=Il.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,as)};P();w();import _l from"node:path";P();import $l from"node:fs/promises";import Ce from"node:path";import kl from"picocolors";w();var Nl=["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 $l.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,Nl),t.text="Formatting code...",await K(e,d,"lint:fix"),t.succeed(kl.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 Al from"picocolors";w();var ps=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(Al.green(d.length>0?`Testing setup repaired (${d.join("; ")}).`:"Testing setup repaired."))}catch(n){throw r.fail("Failed to repair testing setup."),n}};var ls={id:"tests",name:"Tests",description:"Vitest + React Testing Library + jsdom",detect:async e=>l(_l.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,ps)};w();import Ll from"node:path";P();import us from"node:path";import Dl from"picocolors";w();var Fl={npm:"npm run",yarn:"yarn",pnpm:"pnpm",bun:"bun run"},Ol=e=>{let t=Fl[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=Ol(t)),!i.tsx&&!Object.hasOwn(e.dependencies??{},"tsx")&&(i.tsx="*"),{...e,scripts:r,devDependencies:i}};var ms=`import * as fs from 'node:fs';
962
- import * as path from 'node:path';
963
-
964
- const checkOnly = process.argv.includes('--check');
965
-
966
- const envPath = path.join(process.cwd(), '.env');
967
- const examplePath = path.join(process.cwd(), '.env.example');
968
-
969
- if (!fs.existsSync(envPath)) {
970
- console.log('\u26A0\uFE0F No .env file found. Skipping sync.');
971
- process.exit(0);
972
- }
973
-
974
- const envContent = fs.readFileSync(envPath, 'utf-8');
975
- const lines = envContent.split('\\n');
976
-
977
- let isPublicBlock = false;
978
-
979
- const exampleLines = lines.map((line) => {
980
- const trimmed = line.trim();
981
-
982
- // Preserve empty lines and reset public block state
983
- if (!trimmed) {
984
- isPublicBlock = false;
985
- return line;
986
- }
987
-
988
- // Preserve comments
989
- if (trimmed.startsWith('#')) {
990
- if (line.toLowerCase().includes('-public')) {
991
- isPublicBlock = true;
992
- }
993
- return line;
994
- }
995
-
996
- // Parse KEY=VALUE
997
- const match = line.match(/^([^=]+)=(.*)$/);
998
-
999
- if (match) {
1000
- const key = match[1];
750
+ packages/ shared libraries (see below)
751
+ turbo.json task graph and caching
752
+ pnpm-workspace.yaml workspace members and install policy
753
+ \`\`\`
1001
754
 
1002
- // Public marker: explicit "# -public" comment, or "KEY=VAL # -public"
1003
- if (isPublicBlock || line.toLowerCase().includes('-public')) {
1004
- isPublicBlock = false;
1005
- return line;
1006
- }
755
+ ## Getting started
1007
756
 
1008
- isPublicBlock = false;
1009
- return \`\${key}=\`;
1010
- }
757
+ \`\`\`bash
758
+ pnpm install
759
+ pnpm dev # every app
760
+ pnpm --filter ${e.apps[0]} dev # one app
761
+ \`\`\`
1011
762
 
1012
- isPublicBlock = false;
1013
- return line;
1014
- });
763
+ 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\`).
1015
764
 
1016
- const newExampleContent = exampleLines.join('\\n');
765
+ ## Scripts
1017
766
 
1018
- let currentExampleContent = '';
1019
- if (fs.existsSync(examplePath)) {
1020
- currentExampleContent = fs.readFileSync(examplePath, 'utf-8');
1021
- }
767
+ | Script | Purpose |
768
+ | :-- | :-- |
769
+ | \`pnpm build\` | build every app (Turborepo caches unchanged apps) |
770
+ | \`pnpm lint\` / \`pnpm ci:check\` | Biome per app and at the root |
771
+ | \`pnpm type-check\`, \`pnpm check:deprecated\`, \`pnpm test\`, \`pnpm test:e2e\` | per-app gates through Turborepo |
772
+ | \`pnpm validate\` | everything CI runs |
1022
773
 
1023
- if (currentExampleContent === newExampleContent) {
1024
- console.log('\u2728 .env.example is already up to date');
1025
- process.exit(0);
1026
- }
774
+ ## Adding a shared package
1027
775
 
1028
- if (checkOnly) {
1029
- console.error('\u274C .env.example is out of sync with .env. Run "yarn env:sync" to update.');
1030
- process.exit(1);
1031
- }
776
+ \`\`\`bash
777
+ mkdir -p packages/ui && cd packages/ui
778
+ pnpm init # name it @${e.name}/ui, "type": "module"
779
+ cd ../../apps/${e.apps[0]} && pnpm add @${e.name}/ui --workspace
780
+ \`\`\`
1032
781
 
1033
- fs.writeFileSync(examplePath, newExampleContent);
1034
- console.log('\u2705 Synchronized .env.example with .env');
1035
- `,ds=`/**
1036
- * Walks every identifier in the project and flags any whose resolved TypeScript
1037
- * symbol carries a \`@deprecated\` JSDoc tag. Catches deprecated API usage that
1038
- * \`tsc --noEmit\` does not surface (deprecations are "suggestion"-level, not
1039
- * errors) and that Biome does not yet detect.
1040
- *
1041
- * Exits 1 if any deprecated usage is found (so it can gate CI).
1042
- */
1043
- import path from 'node:path';
1044
- import ts from 'typescript';
782
+ 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.
1045
783
 
1046
- const cwd = process.cwd();
1047
- const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, 'tsconfig.json');
784
+ ## Adding an app
1048
785
 
1049
- if (!configPath) {
1050
- console.error('tsconfig.json not found');
1051
- process.exit(1);
1052
- }
786
+ \`\`\`bash
787
+ cd apps && npx @teispace/next-maker init <name> --yes --set hooks=false --set commitizen=false --set ci=false --set docker=false --no-git
788
+ \`\`\`
1053
789
 
1054
- const raw = ts.readConfigFile(configPath, ts.sys.readFile).config;
1055
- const parsed = ts.parseJsonConfigFileContent(raw, ts.sys, path.dirname(configPath));
790
+ Git hooks, CI, and Docker live at the workspace root; apps keep their own tests, lint, and type-check.
1056
791
 
1057
- const program = ts.createProgram({
1058
- rootNames: parsed.fileNames,
1059
- options: parsed.options,
1060
- });
792
+ ## Docker
1061
793
 
1062
- const checker = program.getTypeChecker();
794
+ \`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:
1063
795
 
1064
- function isDeprecated(sym: ts.Symbol | undefined): boolean {
1065
- if (!sym) return false;
1066
- return sym.getJsDocTags(checker).some((tag) => tag.name === 'deprecated');
1067
- }
796
+ \`\`\`bash
797
+ docker build -f apps/${e.apps[0]}/Dockerfile -t ${e.apps[0]} .
798
+ docker compose up --build
799
+ \`\`\`
1068
800
 
1069
- let hits = 0;
1070
-
1071
- for (const sourceFile of program.getSourceFiles()) {
1072
- if (sourceFile.fileName.includes('node_modules')) continue;
1073
- if (!sourceFile.fileName.startsWith(cwd)) continue;
1074
-
1075
- const visit = (node: ts.Node): void => {
1076
- if (ts.isIdentifier(node)) {
1077
- const sym = checker.getSymbolAtLocation(node);
1078
- if (isDeprecated(sym)) {
1079
- const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
1080
- console.log(
1081
- \`\${path.relative(cwd, sourceFile.fileName)}:\${line + 1}:\${character + 1} \${node.text} (@deprecated)\`,
1082
- );
1083
- hits++;
1084
- }
1085
- }
1086
- ts.forEachChild(node, visit);
1087
- };
801
+ ## Dependencies
802
+
803
+ 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>\`).
804
+
805
+ ## Maintenance
806
+
807
+ \`cd apps/<app> && npx @teispace/next-maker doctor --compile\` checks an app against the starter; \`setup --set <option>=<value>\` changes its features.
808
+ `,Is=()=>`name: CI
809
+
810
+ on:
811
+ push:
812
+ branches: [main]
813
+ pull_request:
814
+ branches: [main]
815
+
816
+ concurrency:
817
+ group: \${{ github.workflow }}-\${{ github.ref }}
818
+ cancel-in-progress: \${{ github.ref != 'refs/heads/main' }}
819
+
820
+ env:
821
+ NEXT_PUBLIC_APP_URL: https://ci.example.com
822
+ NEXT_TELEMETRY_DISABLED: 1
823
+ TURBO_TELEMETRY_DISABLED: 1
824
+
825
+ jobs:
826
+ validate:
827
+ name: Lint, types, tests, build
828
+ runs-on: ubuntu-latest
829
+ timeout-minutes: 20
830
+ steps:
831
+ - uses: actions/checkout@v6
832
+ - uses: pnpm/action-setup@v4
833
+ - uses: actions/setup-node@v6
834
+ with:
835
+ node-version-file: .nvmrc
836
+ cache: pnpm
837
+ - run: pnpm install --frozen-lockfile
838
+ - name: Restore Turborepo cache
839
+ uses: actions/cache@v4
840
+ with:
841
+ path: .turbo
842
+ key: turbo-\${{ runner.os }}-\${{ github.sha }}
843
+ restore-keys: |
844
+ turbo-\${{ runner.os }}-
845
+ - run: pnpm ci:check
846
+ - run: pnpm type-check
847
+ - run: pnpm check:deprecated
848
+ - run: pnpm test
849
+ - run: pnpm build
850
+ `,Ns=()=>`const config = {
851
+ '*': ['biome check --write --no-errors-on-unmatched --files-ignore-unknown=true'],
852
+ };
1088
853
 
1089
- visit(sourceFile);
1090
- }
854
+ export default config;
855
+ `,Fs=()=>`const config = {
856
+ extends: ['@commitlint/config-conventional'],
857
+ rules: {
858
+ 'header-max-length': [2, 'always', 150],
859
+ 'scope-enum': [0],
860
+ },
861
+ };
1091
862
 
1092
- if (hits === 0) {
1093
- console.log('\u2713 No deprecated API usage found.');
1094
- process.exit(0);
1095
- }
863
+ export default config;
864
+ `,Ms=()=>({"pre-commit":`#!/usr/bin/env sh
865
+ pnpm lint-staged
866
+ `,"commit-msg":`#!/usr/bin/env sh
867
+ pnpm exec commitlint --edit "$1"
868
+ `,"pre-push":`#!/usr/bin/env sh
869
+ pnpm ci:check && pnpm type-check && pnpm check:deprecated && pnpm test
870
+ `}),_s=(e,t="24")=>`# syntax=docker.io/docker/dockerfile:1
871
+ # Build from the workspace root:
872
+ # docker build -f apps/${e}/Dockerfile -t ${e} .
873
+
874
+ FROM node:${t}-alpine AS base
875
+ ENV PNPM_HOME=/pnpm
876
+ ENV PATH=$PNPM_HOME:$PATH
877
+ RUN corepack enable && apk add --no-cache libc6-compat
878
+
879
+ # Prune the workspace down to this app and its dependencies.
880
+ FROM base AS pruner
881
+ WORKDIR /app
882
+ RUN pnpm add -g turbo@2
883
+ COPY . .
884
+ RUN turbo prune ${e} --docker
885
+
886
+ # Install with only the pruned lockfile so dependency layers cache well.
887
+ FROM base AS installer
888
+ WORKDIR /app
889
+ COPY --from=pruner /app/out/json/ ./
890
+ COPY --from=pruner /app/pnpm-workspace.yaml ./
891
+ COPY --from=pruner /app/.npmrc ./
892
+ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
893
+
894
+ # Build with the full pruned source.
895
+ FROM base AS builder
896
+ WORKDIR /app
897
+ ARG NEXT_PUBLIC_API_URL
898
+ ARG NEXT_PUBLIC_APP_URL
899
+ ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
900
+ ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
901
+ ENV NEXT_TELEMETRY_DISABLED=1
902
+ ENV TURBO_TELEMETRY_DISABLED=1
903
+ ENV BUILD_STANDALONE=true
904
+ COPY --from=installer /app/ ./
905
+ COPY --from=pruner /app/out/full/ ./
906
+ RUN pnpm turbo run build --filter=${e}
907
+
908
+ FROM base AS runner
909
+ WORKDIR /app
910
+ ENV NODE_ENV=production
911
+ ENV NEXT_TELEMETRY_DISABLED=1
912
+ ENV PORT=3000
913
+ ENV HOSTNAME=0.0.0.0
914
+ RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
915
+ COPY --from=builder --chown=nextjs:nodejs /app/apps/${e}/public ./apps/${e}/public
916
+ COPY --from=builder --chown=nextjs:nodejs /app/apps/${e}/.next/standalone ./
917
+ COPY --from=builder --chown=nextjs:nodejs /app/apps/${e}/.next/static ./apps/${e}/.next/static
918
+ USER nextjs
919
+ EXPOSE 3000
920
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
921
+ CMD wget -qO- http://127.0.0.1:3000/robots.txt >/dev/null || exit 1
922
+ CMD ["node", "apps/${e}/server.js"]
923
+ `,Ds=e=>`services:
924
+ ${e.map((r,n)=>` ${r}:
925
+ build:
926
+ context: .
927
+ dockerfile: apps/${r}/Dockerfile
928
+ args:
929
+ NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL}
930
+ NEXT_PUBLIC_APP_URL: \${${r.toUpperCase().replace(/-/g,"_")}_URL:-http://localhost:${3e3+n}}
931
+ restart: unless-stopped
932
+ ports:
933
+ - "${3e3+n}:3000"
934
+ environment:
935
+ NODE_ENV: production
936
+ NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL}
937
+ API_INTERNAL_URL: \${API_INTERNAL_URL}`).join(`
1096
938
 
1097
- console.log(\`\\n\${hits} deprecated symbol use(s) found.\`);
1098
- process.exit(1);
1099
- `,Ct=[{relative:"scripts/sync-env.ts",content:ms},{relative:"scripts/check-deprecated.ts",content:ds}];var xr=async e=>{let t=S("Adding validation scripts...");try{let r=!1;for(let{relative:o,content:s}of Ct){let c=us.join(e,o);l(c)||(await h(c,s),r=!0)}let i=await A(e),n=!1;await _(us.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(Dl.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 fs from"node:path";import jl from"picocolors";w();var gs=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=fs.join(e,p);l(d)||(await h(d,u),i.push(p))}let n=await B(e,t),o=fs.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(jl.green(c.length>0?`Validation scripts repaired (${c.join("; ")}).`:"Validation scripts repaired."))}catch(i){throw r.fail("Failed to repair validation scripts."),i}};var hs=["scripts/sync-env.ts","scripts/check-deprecated.ts"],ys={id:"validate-scripts",name:"Validation Scripts",description:"sync-env, check-deprecated, and the validate chain",detect:async e=>hs.every(t=>l(Ll.join(e,t))),files:hs.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,gs)};import{access as Bl,readFile as zl}from"node:fs/promises";import Ts from"node:path";P();import gi from"node:path";import Ml from"picocolors";w();P();import ui from"node:fs/promises";import Rt from"node:path";w();var xs=async(e,t)=>{t.text="Downloading assets from starter repository...",await J(e)},ws=async(e,t,r)=>{let i=Rt.join(e,a.WS_UTILS);await ui.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 fi(i)},fi=async e=>{let t=async r=>{let i=await ui.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 ui.unlink(o)}};await t(e)};P();w();import Ps from"node:path";var vs=async e=>{let t=Ps.join(e,a.WS_UTILS);return l(t)?{isSetup:!0,reason:"src/lib/utils/ws/ already exists"}:{isSetup:!1}},Ss=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(Ps.join(e,i))||t.push(`- ${n}`);if(t.length>0)throw new Error(`WebSocket setup requires Redux Toolkit. Missing:
1100
- ${t.join(`
1101
939
  `)}
1102
-
1103
- Run \`next-maker setup --redux\` first.`)};var wr=async e=>{let t=S("Setting up WebSocket layer..."),r=gi.join(e,".next-maker-temp-ws");try{let{isSetup:i,reason:n}=await vs(e);if(i){t.fail(`WebSocket layer is already set up (${n}).`);return}await Ss(e),await xs(r,t),t.text="Copying WebSocket files...";let o=l(gi.join(e,a.VITEST_CONFIG));await ws(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=gi.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(Ml.green("WebSocket layer setup successfully!"))}catch(i){throw t.fail("Failed to setup WebSocket layer."),i}finally{await v(r)}};P();import Gl from"node:fs/promises";import We from"node:path";import Hl from"picocolors";w();var Ul="socket.io-client@^4.8.3",Es=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 Gl.cp(We.join(x,a.WS_UTILS),n,{recursive:!0,force:!0}),f||await fi(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,Ul),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(Hl.green(d.length>0?`WebSocket layer repaired (${d.join("; ")}).`:"WebSocket layer repaired."))}catch(n){throw r.fail("Failed to repair WebSocket layer."),n}};var Vl=async e=>{try{let t=JSON.parse(await zl(Ts.join(e,"package.json"),"utf-8"));return{...t.dependencies,...t.devDependencies}["socket.io-client"]?(await Bl(Ts.join(e,"src/lib/utils/ws")),!0):!1}catch{return!1}},Cs={id:"ws",name:"WebSocket Client",description:"socket.io-client + lazy WS singleton + Redux slice + hooks",detect:Vl,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,Es)};P();w();import Oe from"node:path";var Rs=async e=>{let t=Oe.join(e,a.PACKAGE_JSON);return l(t)?JSON.parse(await g(t)):null},Jl=async(e,t)=>{let r=Oe.join(e,a.PACKAGE_JSON);await h(r,`${JSON.stringify(t,null,2)}
1104
- `)},bs=(e,t)=>(t==="dependency"?e.dependencies:e.devDependencies)??{},Is=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 Rs(t);if(n){for(let s of e.packages)bs(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=Is(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 Is(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 Rs(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)bs(s,p.kind)[p.name]&&n.packagesUninstalled.push(p.name);c&&!i&&await Jl(t,s)}if(!i&&n.packagesUninstalled.length>0){let c=await A(t);await kr(t,c,n.packagesUninstalled)}return n};var De=[cs,ys,Po,qo,yo,ls,ko,jo,ns,Cs,Vo],$s=e=>De.find(t=>t.id===e);var ks=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}`}},Wl=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}},ql={fixed:$.green("\u2713"),stillDrifted:$.red("\u2717"),noApply:$.yellow("\u26A0\uFE0F")},Xl={fixed:$.green("FIXED"),stillDrifted:$.red("STILL DRIFTED"),noApply:$.yellow("NO AUTOMATIC FIX AVAILABLE")},Yl=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},Ns=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(`
1105
- \u{1FA7A} Project Doctor
1106
- `));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")} ${ks(I)}`)}let o=Wl(n);m(""),m($.dim(`${o.cleanCount} clean, ${o.driftedCount} drifted, ${n.length-o.installedCount} not installed`)),o.driftedCount===0&&(m($.green(`
1107
- \u2713 No drift detected.
1108
- `)),process.exit(0)),t.fix||(m($.dim(`
1109
- Run with --fix to re-apply drifted features.
1110
- `)),process.exit(1)),m($.cyan(`
1111
- \u{1F527} Applying fixes...
1112
- `));let s=await Yl(o.driftedResults,r);m("");for(let u of s){m(` ${ql[u.status]} ${u.name} ${Xl[u.status]}`);for(let d of u.remaining)m(` ${$.yellow("\u2022")} ${ks(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(`
1113
- ! Doctor could not fix everything.
1114
- `)),process.exit(1)),m($.green(`
1115
- \u2713 All drift fixed.
1116
- `)),process.exit(0)}catch(r){E(`${r instanceof Error?r.message:r}`),process.exit(1)}})};import Xe from"picocolors";P();w();import hi from"node:path";var Kl=/^[A-Z][A-Z0-9_]*$/,Fs=e=>{if(!Kl.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,yi=e=>e.public?"client":"server",Zl=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,"\\'")}')`}},Ql=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()"}},Os=e=>{let t=ye(e.name,e.public),r=Ql(e),i=e.default!==void 0,n=!e.required&&!i;if(i&&(r+=Zl(e)),n&&(r+=".optional()"),e.description){let o=e.description.replace(/'/g,"\\'");r+=`
1117
- .describe('${o}')`}return` ${t}: ${r},`},Ds=e=>{let t=ye(e.name,e.public);return` ${t}: process.env.${t},`},em=e=>new RegExp(`(${e}:\\s*\\{)([\\s\\S]*?)(\\n\\s*\\})`),As=/(runtimeEnv:\s*\{)([\s\S]*?)(\n\s*\})/,_s=(e,t,r,i)=>{let n=t.replace(/\s+$/,""),o=n.length===0||/,\s*$/.test(n)?n:`${n},`;return`${e}${o}
1118
- ${i}
1119
- ${r.replace(/^\n/,"")}`},js=(e,t)=>{let r=ye(t.name,t.public),i=yi(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=em(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=Os(t),f=e.replace(o,_s(c,p,u,d)),x=f.match(As);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(As,_s(I,H,k,Ds(t))),f},tm="# -public",rm=e=>{let t=ye(e.name,e.public),r=e.default??"",i=e.public?` ${tm}`:"";return`${t}=${r}${i}`},Ls=(e,t)=>{let r=ye(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let n=e===""||e.endsWith(`
1120
- `)?"":`
1121
- `,o=e===""?"":`
1122
- `,s=t.description?`# ${t.description}
1123
- `:"";return`${e}${n}${o}${s}${rm(t)}
1124
- `},Ms=(e,t)=>{let r=ye(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let n=e===""||e.endsWith(`
1125
- `)?"":`
1126
- `,o=t.default??"";return`${e}${n}${r}=${o}
1127
- `},xi=async(e,t)=>{Fs(t.name);let r=hi.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=js(i,t),o=n!==i;o&&await h(r,n);let s=hi.join(e,a.ENV_EXAMPLE),c=!1;if(l(s)){let d=await g(s),f=Ls(d,t);c=f!==d,c&&await h(s,f)}let p=hi.join(e,".env"),u=!1;if(l(p)){let d=await g(p),f=Ms(d,t);u=f!==d,u&&await h(p,f)}return{schemaUpdated:o,envExampleUpdated:c,envUpdated:u,group:yi(t)}};var Gs=new Set(["string","url","number","boolean","enum"]),im=e=>{let t=(e??"string").toLowerCase();if(!Gs.has(t))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(Gs).join(", ")}.`);return t},nm=e=>{if(!e)return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length===0?void 0:t},Hs=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(`
1128
- \u{1F510} Env Var Generator
1129
- `)),r.required&&r.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let n=im(r.type),o=nm(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 xi(i,s);y.succeed("Env var written");let p=ye(s.name,s.public);m(Xe.green(`
1130
- \u2728 ${p} declared in the ${c.group} group.
1131
- `)),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 om from"enquirer";var{prompt:sm}=om,Us=async(e={})=>e.source?{source:e.source}:{source:(await sm({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 am,readFile as cm,stat as pm}from"node:fs/promises";import je from"node:path";var Bs=async e=>{try{return await am(e),!0}catch{return!1}},lm=async e=>{try{return(await pm(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 lm(r))return r;return null},mm=["next-pwa","@ducanh2912/next-pwa","@serwist/next","serwist"],wi=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 Bs(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 Bs(o)){t.appManifestPath=o,t.isPwa=!0;break}}try{let n=JSON.parse(await cm(je.join(e,"package.json"),"utf-8")),o={...n.dependencies,...n.devDependencies};for(let s of mm)if(o[s]){t.framework=s,t.isPwa=!0;break}}catch{}return t};import{mkdir as hm,readFile as Qs,stat as ym,writeFile as xm}from"node:fs/promises";import me from"node:path";import wm from"sharp";import um from"png-to-ico";import bt from"sharp";var zs=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),Pi=(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 dm(t)}},dm=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="${zs(i)}" fill="white"/></svg>`},Vs=(e,t)=>`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${e}"><rect width="${e}" height="${e}" fill="${zs(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 vi(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,ka=Buffer.from(Pi(kt,t,s));k=await bt(k).composite([{input:ka,blend:"dest-in"}]).png({compressionLevel:Js(c)}).toBuffer()}else k=await bt(k).png({compressionLevel:Js(c)}).toBuffer();return k},vi=async e=>{let t=await bt(Buffer.from(Vs(1,e))).raw().toBuffer({resolveWithObject:!0}),[r,i,n,o=255]=t.data;return{r,g:i,b:n,alpha:o/255}},Js=e=>{let t=Math.round((100-e)/100*9);return Math.max(0,Math.min(9,t))},Ws=async e=>{if(e.length===0)throw new Error("No frames provided to encodeIco");return um(e)};import{readFile as fm,writeFile as VT}from"node:fs/promises";var qs=e=>e.map(({src:t,size:r})=>({src:t,sizes:`${r}x${r}`,type:"image/png",purpose:"any maskable"})),Xs=async(e,t)=>{let r=await fm(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)}
1132
- `};var Ys=e=>`icons: [
1133
- ${e.map(r=>` { src: '${r.src}', sizes: '${r.sizes}', type: '${r.type}'${r.purpose?`, purpose: '${r.purpose}'`:""} }`).join(`,
1134
- `)},
1135
- ],`,Ks=(e,t,r)=>`${JSON.stringify({name:e,short_name:e,start_url:"/",display:"standalone",background_color:"#ffffff",theme_color:t,icons:r},null,2)}
1136
- `;import{stat as gm}from"node:fs/promises";import Si from"node:path";var Zs=[".png",".jpg",".jpeg",".webp",".svg",".avif"],Sr=async e=>{if(!e)throw new Error("Source image path is required (use --path).");let t=Si.isAbsolute(e)?e:Si.resolve(process.cwd(),e),r;try{r=await gm(t)}catch{throw new Error(`Source image not found: ${t}`)}if(!r.isFile())throw new Error(`Source is not a file: ${t}`);let i=Si.extname(t).toLowerCase();if(!Zs.includes(i))throw new Error(`Unsupported source format '${i}'. Use one of: ${Zs.join(", ")}`);return{absolutePath:t,ext:i,bytes:r.size,isVector:i===".svg"}};var Ei=(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 Pm=async e=>{try{return await ym(e),!0}catch{return!1}},vm=async(e,t,r)=>{let i=[];for(let n of e){if(await Pm(n.outPath)&&!t&&!n.alwaysWrite){i.push({path:n.outPath,bytes:n.bytes.length,skipped:!0});continue}r||(await hm(me.dirname(n.outPath),{recursive:!0}),await xm(n.outPath,n.bytes)),i.push({path:n.outPath,bytes:n.bytes.length})}return i},Ti=async(e,t)=>{let r=await Sr(t.source),i=await Qs(r.absolutePath),n=t.ogSource?await Qs((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 Ws(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 ea(n,t,!1);o.push({outPath:me.join(t.outDir,"opengraph-image.png"),bytes:d})}if(t.emitTwitter){let d=await ea(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 wi(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=qs(I);if(d.publicManifestPath){let k=await Xs(d.publicManifestPath,H);o.push({outPath:d.publicManifestPath,bytes:Buffer.from(k,"utf-8"),alwaysWrite:!0})}else if(d.appManifestPath)p=Ys(H);else if(t.pwaInit){let k=me.join(f,"manifest.webmanifest"),Z=Ks(me.basename(e)||"App","#000000",H);o.push({outPath:k,bytes:Buffer.from(Z,"utf-8")})}}return{outputs:await vm(o,t.force,t.dryRun),pwaSnippet:p}},ea=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 vi(c),u=Math.round((1200-o)/2),d=Math.round((n-o)/2);return wm({create:{width:1200,height:n,channels:4,background:p}}).composite([{input:s,left:u,top:d}]).png().toBuffer()};var ta=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 Sm=["square","rounded","circle","squircle"],Em=["contain","cover","clip"],ra=(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},Ci=(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},na=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(`
1137
- \u{1F3A8} Favicon Generator
1138
- `));let i=await Us({source:t.path}),n=await Tm(r,t.out),o=ra(t.shape,Sm,"square","shape"),s=ra(t.fit,Em,"contain","fit"),c=Ci(t.radius,20,"radius",0,50),p=Ci(t.padding,0,"padding",0,30),u=Ci(t.quality,90,"quality",1,100),d=Ei(t.sizes,[16,32,48]),f=t.bg??"transparent";bm(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 Ti(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} (${ia(k.bytes)})`)):m(Le.green(` \u2713 ${Z} `)+Le.dim(`(${ia(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)}})},Tm=async(e,t)=>{if(t)return Er.isAbsolute(t)?t:Er.resolve(e,t);let r=await vr(e);return r||(Gi("Could not detect app/ or src/app/ \u2014 falling back to src/app"),Er.join(e,"src","app"))},Cm=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,Rm=/^(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/]+\)$/i,bm=e=>{if(e!=="transparent"&&!Cm.test(e)&&!Rm.test(e)&&!ta.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().`)},ia=e=>e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`;import pa from"node:path";import V from"picocolors";q();q();import Hm from"node:path";import{readFile as Im,writeFile as $m}from"node:fs/promises";import km from"node:path";var It=async e=>{let{serviceName:t,projectPath:r}=e,i=G(t),n=km.join(r,"src","lib","config","app-apis.ts");try{let o=await Im(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}: {
1139
- base: '/${t}',
1140
- getAll: '/${t}',
1141
- },`;o=ot(o,s),await $m(n,o)}catch(o){throw new Error(`Failed to register API endpoints: ${o}`,{cause:o})}};import{readFile as Nm,writeFile as Am}from"node:fs/promises";import _m from"node:path";w();var Ri=async(e,t,r)=>{let i=_m.join(e,"src","lib","config","app-paths.ts");if(!l(i))return;let n=await Nm(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}',
1142
- `;n=n.slice(0,s)+c+n.slice(s),await Am(i,n)};P();w();import Tr from"node:path";var Fm=e=>new RegExp(`<\\s*${e}\\b`),oa=(e,t)=>{if(Fm(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}>
1143
- ${o}{children}
1144
- ${n}</${t}>`;return e.replace(r,s)},sa=(e,t)=>{let r=`export * from './${t}';`;if(e.includes(r))return e;let i=e.split(`
1145
- `),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}
1146
- ${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(`
1147
- `)},Om=a.PROVIDERS,Dm=a.ROOT_PROVIDER,jm=a.PROVIDERS_INDEX,aa=async(e,t={fileExists:l,readFile:g})=>{let r=Tr.join(e,Dm);if(t.fileExists(r))return r;let i=Tr.join(e,Om);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},bi=async e=>{let{projectPath:t,componentName:r,fileBaseName:i}=e,n=await aa(t);if(n){let c=await g(n),p=c;(!/from '\.\//.test(c)||!c.includes(`{ ${r} }`))&&(p=nt(p,`import { ${r} } from './${i}';`)),p=oa(p,r),p!==c&&await h(n,p)}let o=Tr.join(t,jm),s=null;if(l(o)){let c=await g(o),p=sa(c,i);p!==c&&await h(o,p),s=o}return{rootProviderFile:n,barrelFile:s}};w();import{readFile as Lm,writeFile as Mm}from"node:fs/promises";import Gm from"node:path";var Ii=async(e,t)=>{let r=Gm.join(e,"src","i18n","translations","en.json");if(!l(r))return;let i=await Lm(r,"utf-8"),n=JSON.parse(i);n[t]||(n[t]={title:t,description:`${t} page description`},await Mm(r,`${JSON.stringify(n,null,2)}
1148
- `))};var $i=()=>[{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 qr({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:Hm.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 ki=()=>[{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 Um from"node:path";var Ni=()=>[{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:Um.join(e.basePath,e.sliceName)})}}];import Bm from"enquirer";var{prompt:zm}=Bm,ca=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 zm(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 la=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();Vm(r),m(V.cyan(`
1149
- \u{1F3AF} Feature Generator
1150
- `));let n=$i();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"}
1151
- `));let c=await ca(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||pa.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}),Jm(c,p)}catch(i){y.fail("Feature generation failed"),E(`${i}`),process.exit(1)}})},Vm=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))},Jm=(e,t)=>{let r=pa.join(t,e.featureName);m(V.green(`
1152
- \u2728 Feature '${e.featureName}' created successfully!
1153
- `)),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
1154
- `)),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 Ai from"node:path";import Ke from"picocolors";q();import Wm from"enquirer";var{prompt:qm}=Wm,ma=async e=>e?{hookName:e}:await qm({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 da=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(`
1155
- \u{1FA9D} Hook Generator
1156
- `));let n=await M(i),o=await ma(t);W(o.hookName,"hook name"),r.feature&&pe(r.feature,"feature path");let c=`use${N(o.hookName)}`,p=Xm(r,n.hasTests);y.start("Generating hook..."),await Yr({name:o.hookName,projectPath:i,isClient:r.client??!0,featurePath:r.feature}),y.succeed("Hook generated");let u=r.feature?Ai.join(i,r.feature,"hooks",`${c}.ts`):Ai.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(`
1157
- \u2728 Hook '${c}' created successfully!
1158
- `)),m(Ke.dim(` \u{1F4C4} ${f}`)),d&&m(Ke.dim(` \u{1F9EA} ${Ai.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)}})},Xm=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import Cr from"picocolors";q();var ua=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(`
1159
- \u{1F4D0} Layout Generator
1160
- `)),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"}
1161
- `)),y.start("Generating layout...");let s=await Kr({segment:t,at:r.at,projectPath:i,hasI18n:o,withGroup:!!r.group});y.succeed("Layout generated"),m(Cr.green(`
1162
- \u2728 ${s.componentName} created!
1163
- `)),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 Ym from"enquirer";var{prompt:Km}=Ym,fa=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 Km(r);return{code:e??i.code,name:i.name,country:i.country,flag:i.flag,copyTranslations:t.copyTranslations??i.copyTranslations??!1}};var ga=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(`
1164
- \u{1F30D} Locale Generator
1165
- `)),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(`
1166
- Run: npx @teispace/next-maker setup --i18n
1167
- `)),process.exit(1));let o=await fa(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 Zr({...o,projectPath:i}),y.succeed("Locale added"),m(de.green(`
1168
- \u2728 Locale '${o.code}' (${o.name}) added successfully!
1169
- `)),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 ya from"node:path";import ne from"picocolors";q();import Zm from"enquirer";var{prompt:Qm}=Zm,ha=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 Qm(r):{};return{pageName:e??i.pageName,withLoading:t.loading??i.withLoading??!0,withError:t.error??i.withError??!0}};var xa=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(`
1170
- \u{1F4C4} Page Generator
1171
- `)),y.start("Detecting project setup...");let n=await M(i);y.succeed("Project setup detected"),m(ne.dim(` i18n: ${n.hasI18n?"\u2713":"\u2717"}
1172
- `));let o=await ha(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 ei({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 Ri(i,s,d),y.succeed("Route registered"),n.hasI18n&&(y.start("Adding translation namespace..."),await Ii(i,c),y.succeed("Translation namespace added"));let f=r.dynamic?ya.join(p,s,`[${r.dynamic}]`):ya.join(p,s);m(ne.green(`
1173
- \u2728 Page '${s}' created successfully!
1174
- `)),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 _i from"node:path";import Re from"picocolors";var wa=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(`
1175
- \u{1F50C} Provider Generator
1176
- `)),y.start("Generating provider...");let{componentName:i,fileBaseName:n,providerFile:o}=await ri({name:t,projectPath:r});y.succeed("Provider generated"),y.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:s,barrelFile:c}=await bi({projectPath:r,componentName:i,fileBaseName:n});y.succeed("Provider registered"),m(Re.green(`
1177
- \u2728 ${i} created!
1178
- `)),m(Re.dim("Generated files:")),m(Re.dim(` \u{1F4C4} ${_i.relative(r,o)}`)),s?m(Re.dim(` \u270F\uFE0F ${_i.relative(r,s)} \u2014 wrapped {children}`)):(m(Re.yellow(`
1179
- \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 ${_i.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 ed from"enquirer";import X from"picocolors";var{prompt:td}=ed,rd=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},id=e=>e.filesRemoved.length===0&&e.blocksStripped.length===0&&e.scriptsRemoved.length===0&&e.packagesUninstalled.length===0&&e.manualCleanup.length===0,Pa=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=$s(t);if(!n)throw new Error(`Unknown feature "${t}". Valid: ${De.map(p=>p.id).join(", ")}.`);if(m(X.cyan(`
1180
- \u{1F5D1} Remove ${n.name}
1181
- `)),!(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"),id(s)){m(X.yellow(`
1182
- \u26A0\uFE0F Manifest had no removable artifacts. Nothing to do.
1183
- `));return}m(X.bold(`
1184
- Planned changes:
1185
- `));for(let p of rd(s))m(p);if(m(""),r.dryRun){m(X.dim(`--dry-run set \u2014 exiting without writing.
1186
- `));return}if(!r.yes){let{confirm:p}=await td({type:"confirm",name:"confirm",message:`Apply the changes above to remove ${n.name}?`,initial:!1});if(!p){m(X.yellow(`
1187
- Aborted.
1188
- `));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(`
1189
- \u2713 ${n.name} removed.`)),c.manualCleanup.length>0){m(X.yellow(`
1190
- \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 ad}from"node:fs";import{mkdir as cd}from"node:fs/promises";import $t from"node:path";import oe from"picocolors";q();import{readFile as nd,writeFile as od}from"node:fs/promises";import sd from"node:path";var va=async(e,t)=>{let r=G(t),i=sd.join(e,"src","lib","config","app-apis.ts");try{let n=await nd(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=Gn({camelName:r,kebabName:t});n=ot(n,o),await od(i,n)}catch(n){throw new Error(`Failed to register CRUD API endpoints: ${n}`,{cause:n})}};import Sa from"enquirer";var Ea=async(e,t,r,i)=>{let n=e||(await Sa.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 Sa.prompt({type:"select",name:"httpClient",message:"Choose HTTP client:",choices:["axios","fetch"]})).httpClient:o="axios",{serviceName:n,httpClient:o}};var Ta=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(`
1191
- \u{1F527} Service Generator
1192
- `)),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(`
1193
- Check: lib/utils/http/axios-client or lib/utils/http/fetch-client
1194
- `)),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(", ")}
1195
- `));let s=await Ea(t,r.axios,r.fetch,n.httpClient);W(s.serviceName,"service name"),r.path&&pe(r.path,"path");let{basePath:c,servicePath:p}=pd(i,s.serviceName,r.path);ad(p)||await cd(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 Wr({name:s.serviceName,outputPath:p,httpClient:s.httpClient}),y.succeed("CRUD service files generated"),y.start("Registering CRUD API endpoints..."),await va(i,s.serviceName),y.succeed("CRUD API endpoints registered")):await Me(ki(),{projectPath:i,spinner:y,serviceName:s.serviceName,servicePath:p,httpClient:s.httpClient}),ld(s,c,!!r.crud)}catch(i){y.fail("Service generation failed"),E(`${i}`),process.exit(1)}})},pd=(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)}},ld=(e,t,r=!1)=>{let i=$t.join(t,`${e.serviceName}.service.ts`);m(oe.green(`
1196
- \u2728 ${r?"CRUD ":""}Service '${e.serviceName}' created successfully!
1197
- `)),m(oe.dim("Generated files:")),m(oe.dim(` \u{1F4C4} ${i}
1198
- `)),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 md from"enquirer";import Fi from"picocolors";var{prompt:dd}=md,Oi=[{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}],ud=async(e,t={})=>{let r=t.promptFn??dd,i=t.cwd??(()=>process.cwd());m(Fi.cyan(`
1199
- \u{1F527} Setup Wizard
1200
- `));let n=Oi.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:[...Oi.map(p=>({name:p.key,message:p.label})),{name:"cancel",message:"Cancel"}]}])).feature;if(s==="cancel")return m(Fi.yellow("Setup cancelled.")),null;let c=Oi.find(p=>p.key===s);return c?(await c.handler(i()),c.key):(m(Fi.yellow(`
1201
- \u26A0\uFE0F ${s} setup is not implemented yet.`)),null)},Ca=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 ud(t)}catch(r){y.fail("Setup failed"),E(`${r}`),process.exit(1)}})};import{existsSync as hd}from"node:fs";import{mkdir as yd}from"node:fs/promises";import be from"node:path";import Y from"picocolors";q();import fd from"enquirer";var{prompt:gd}=fd,Ra=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 gd(r):{};return{sliceName:e||i.sliceName,persistSlice:t===void 0?i.persistSlice||!1:t}};var ba=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(`
1202
- \u{1F527} Slice Generator
1203
- `)),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(`
1204
- Run: npm install @reduxjs/toolkit react-redux
1205
- `)),process.exit(1)),m(Y.dim(` Redux: \u2713
1206
- `));let o=await Ra(t,r.persist);W(o.sliceName,"slice name"),r.path&&pe(r.path,"path");let{basePath:s,slicePath:c}=xd(i,o.sliceName,r.path),p=be.join(i,s);hd(p)||await yd(p,{recursive:!0}),await Se(i,o.sliceName,s)&&(E(`Slice '${o.sliceName}' already exists at ${s}!`),process.exit(1)),await Me(Ni(),{projectPath:i,spinner:y,sliceName:o.sliceName,basePath:s,slicePath:c,persistSlice:o.persistSlice});let d=null;if(wd(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")}Pd(o,s,d,i)}catch(i){y.fail("Slice generation failed"),E(`${i}`),process.exit(1)}})},xd=(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)}},wd=(e,t)=>e.test===!1?!1:e.test===!0?!0:t,Pd=(e,t,r,i)=>{let n=be.join(t,e.sliceName);m(Y.green(`
1207
- \u2728 Slice '${e.sliceName}' created successfully!
1208
- `)),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
1209
- `)),r&&m(Y.dim(` \u{1F9EA} ${be.relative(i,r)}
1210
- `)),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 Di from"picocolors";w();q();var Ia=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
1211
- 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(Di.cyan(`
1212
- \u{1F9EA} Test Generator
1213
- `)),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=vd(r.kind)??Sd(n,s);c||(E(`Could not infer test kind for ${se.basename(n)}. Pass --kind component|hook|slice.`),process.exit(1));let p=Ed(n,c),u=Td(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(Di.green(`
1214
- \u2728 Test '${se.basename(d)}' created.
1215
- `)),m(Di.dim(` \u{1F9EA} ${se.relative(i,d)}`)),m("")}catch(i){y.fail("Test generation failed"),E(`${i}`),process.exit(1)}})},vd=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},Sd=(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},Ed=(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)},Td=(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 $a=e=>{Nn(e),Ca(e),la(e),ba(e),Ta(e),xa(e),ua(e),lo(e),ga(e),da(e),wa(e),Hs(e),na(e),Ns(e),Pa(e),Ia(e)};var Rd=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 Rd.apply(process,arguments)};async function bd(){Mi({logger:t=>Nt(t)});let e=new Cd;e.name("next-maker").description("Teispace Next.js Project Generator").version(ji.version),$a(e),e.parse()}bd().catch(e=>{let t=e&&typeof e=="object"&&"message"in e?e.message:String(e);Nt(`Unexpected error: ${t}`)});
940
+ `,Ls=()=>`node_modules
941
+ **/node_modules
942
+ .git
943
+ .turbo
944
+ **/.next
945
+ **/coverage
946
+ **/playwright-report
947
+ **/test-results
948
+ **/.env
949
+ **/.env.*
950
+ `,Us=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)))},Gs=(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},Bs=e=>Object.keys(e).length?`
951
+ catalog:
952
+ ${Object.entries(e).map(([t,r])=>` '${t}': '${r}'`).join(`
953
+ `)}
954
+ `:"";var Cp=(e,t=[])=>[...t,e],Vs={hooks:!1,commitizen:!1,ci:!1,docker:!1,githubTemplates:!1,communityFiles:[]},Hs=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 (${Fe().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 Ap(t,r)}catch(n){b(`${n instanceof Error?n.message:n}`),process.exit(1)}})},Ce=async(e,t,r,n)=>{let o=St(e);n.current=o;try{let s=await r();return o.succeed(t),s}finally{n.current===o&&(n.current=null)}},Op=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)rt(r);if(new Set(t).size!==t.length)throw new Error("App names must be unique.");return t},Ap=async(e,t)=>{Pt(),rt(e);let r=Op(t.apps??"web,admin"),n=v.resolve(process.cwd(),e);if(w(n))throw new Error(`Directory ${e} already exists.`);let o={...t.preset?Me(t.preset):{},...xe(t.set)};for(let[c,l]of Object.entries(Vs))o[c]!==void 0&&JSON.stringify(o[c])!==JSON.stringify(l)&&m(he.yellow(` ! ${c} is managed at the workspace root; ignoring --set ${c}`)),o[c]=l;o.packageManager="pnpm";let s={current:null},i=async()=>{w(n)&&(s.current?.stop(),s.current=null,m(he.yellow(`
955
+ Cleaning up ${e}...`)),await tt(n))},a=$t(async()=>{m(he.red(`
956
+ Process interrupted.`)),await i()});try{await Wt(v.join(n,"apps"),{recursive:!0}),await Wt(v.join(n,"packages"),{recursive:!0});let c=V({starterPath:t.starterPath}),l=v.join(n,"apps",r[0]),p=await Ce(`Fetching starter (${c.location})...`,"Starter fetched.",()=>ve({projectPath:l,source:c,supplied:o,packageManager:"pnpm"}),s);if(!t.yes){let x=await Ct(p.manifest,p.answers);p=nt(p.manifest,{...x,...Vs,packageManager:"pnpm"},"pnpm"),m("")}for(let x of p.forced)m(he.yellow(` ! ${x.reason}; ${x.option} turned off`));let u=JSON.parse(await Kt(v.join(l,"package.json"),"utf-8")),f=await Kt(v.join(l,"pnpm-workspace.yaml"),"utf-8"),d={};for(let x of[".nvmrc",".npmrc",".editorconfig"])w(v.join(l,x))&&(d[x]=await Kt(v.join(l,x),"utf-8"));let g=x=>me(x,{projectName:x,description:`${x} app of ${e}`,readme:!0,copyEnv:!0});await Ce(`Composing apps/${r[0]}...`,`apps/${r[0]} composed.`,()=>$e(l,g(r[0]),p,c),s);for(let x of r.slice(1)){let E=v.join(n,"apps",x);await Ce(`Composing apps/${x}...`,`apps/${x} composed.`,async()=>{let R=await ve({projectPath:E,source:c,supplied:p.answers,packageManager:"pnpm"});await $e(E,g(x),R,c)},s)}let y=[];for(let x of r){let E=v.join(n,"apps",x);for(let U of["pnpm-workspace.yaml","pnpm-lock.yaml",".npmrc",".nvmrc"])await jp(v.join(E,U),{force:!0});let R=JSON.parse(await Kt(v.join(E,"package.json"),"utf-8"));delete R.packageManager,delete R.engines,y.push(R)}let S=t.catalog===!1?{}:Us(y);for(let[x,E]of r.entries()){let R=Gs(y[x],S);await N(v.join(n,"apps",E,"package.json"),`${JSON.stringify(R,null,2)}
957
+ `),t.docker&&await N(v.join(n,"apps",E,"Dockerfile"),_s(E))}await Ce("Writing workspace root...","Workspace root written.",async()=>{let x={name:e,description:`${e} workspace`,author:u.author??"",apps:r,packageManager:"pnpm",packageManagerPin:u.packageManager??"pnpm@11.25.0",nodeEngine:u.engines?.node??">=24.0.0",turboVersion:"^2.10.0",biomeVersion:u.devDependencies?.["@biomejs/biome"]??"^2.5.0",hooks:t.hooks!==!1,huskyVersion:u.devDependencies?.husky,lintStagedVersion:u.devDependencies?.["lint-staged"],commitlintCliVersion:u.devDependencies?.["@commitlint/cli"],commitlintConfigVersion:u.devDependencies?.["@commitlint/config-conventional"]};await N(v.join(n,"package.json"),Ts(x)),await N(v.join(n,"pnpm-workspace.yaml"),Rs(f)+Bs(S)),await N(v.join(n,"turbo.json"),js()),await N(v.join(n,"biome.json"),Cs(x.biomeVersion)),await N(v.join(n,".gitignore"),Os()),await N(v.join(n,"README.md"),As(x,p.manifest.starter.version)),await N(v.join(n,"packages",".gitkeep"),"");for(let[E,R]of Object.entries(d))await N(v.join(n,E),R);if(t.docker&&(await N(v.join(n,"docker-compose.yml"),Ds(r)),await N(v.join(n,".dockerignore"),Ls())),t.ci!==!1&&(await Wt(v.join(n,".github","workflows"),{recursive:!0}),await N(v.join(n,".github","workflows","ci.yml"),Is())),t.hooks!==!1){await N(v.join(n,".lintstagedrc.mjs"),Ns()),await N(v.join(n,"commitlint.config.mjs"),Fs()),await Wt(v.join(n,".husky"),{recursive:!0});for(let[E,R]of Object.entries(Ms())){let U=v.join(n,".husky",E);await N(U,R),await Tp(U,493)}}await N(v.join(n,".next-maker.json"),`${JSON.stringify({workspace:!0,apps:r,starter:p.manifest.starter,answers:p.answers,docker:!!t.docker,catalog:t.catalog!==!1},null,2)}
958
+ `)},s),t.install!==!1&&(await Ce("Installing dependencies with pnpm...","Dependencies installed.",()=>le(n,"pnpm"),s),await Ce("Formatting...","Formatted.",()=>jt(n,"pnpm","lint:fix"),s));for(let x of r){let E=v.join(n,"apps",x,".env.example");w(E)&&await Rp(E,v.join(n,"apps",x,".env"))}t.git!==!1&&await Ce("Initializing git...","Git initialized.",()=>Rt(n),s),a(),m(""),m(he.green(`\u2728 Workspace ${e} is ready with ${r.map(x=>`apps/${x}`).join(", ")}.`)),m(""),m("Next:"),m(he.cyan(` cd ${e}`)),t.install===!1&&m(he.cyan(" pnpm install")),m(he.cyan(` pnpm --filter ${r[0]} dev`)),m("")}catch(c){throw a(),s.current?.fail("Failed to create workspace."),s.current=null,await i(),c}};var qs=e=>{Dn(e),Hs(e),ks(e),is(e),$s(e),es(e),Ss(e),ms(e),cs(e),ns(e),Oo(e),ss(e),rs(e),ps(e),No(e),Yo(e),Ao(e),ls(e),Es(e)};var Np=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 Np.apply(process,arguments)};async function Fp(){gn({logger:t=>kt(t)});let e=new Ip;e.name("next-maker").description("Teispace Next.js Project Generator").version(Ae.version),qs(e),e.parse()}Fp().catch(e=>{let t=e&&typeof e=="object"&&"message"in e?e.message:String(e);kt(`Unexpected error: ${t}`)});