@teispace/next-maker 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +3 -1
- package/dist/index.js +217 -211
- package/dist/index.js.map +4 -4
- package/dist/src/commands/app.d.ts.map +1 -1
- package/dist/src/manifests/bundle-analyzer.manifest.d.ts.map +1 -1
- package/dist/src/manifests/dark-theme.manifest.d.ts.map +1 -1
- package/dist/src/manifests/http-client.manifest.d.ts.map +1 -1
- package/dist/src/manifests/i18n.manifest.d.ts.map +1 -1
- package/dist/src/manifests/redux.manifest.d.ts.map +1 -1
- package/dist/src/manifests/runner.d.ts.map +1 -1
- package/dist/src/manifests/tests.manifest.d.ts.map +1 -1
- package/dist/src/manifests/transforms/next-config.d.ts +25 -0
- package/dist/src/manifests/transforms/next-config.d.ts.map +1 -0
- package/dist/src/manifests/transforms/unwrap-jsx.d.ts +32 -0
- package/dist/src/manifests/transforms/unwrap-jsx.d.ts.map +1 -0
- package/dist/src/manifests/types.d.ts +30 -3
- package/dist/src/manifests/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`),r.includes("axios")&&!
|
|
4
|
-
`),await f(t,
|
|
5
|
-
`,await f(r,t))}},
|
|
2
|
+
var di=Object.defineProperty;var Pe=(e,r)=>()=>(e&&(r=e(e=0)),r);var St=(e,r)=>{for(var t in r)di(e,t,{get:r[t],enumerable:!0})};import{existsSync as wt}from"node:fs";import de from"node:fs/promises";import Tr from"node:path";var u,f,_,T,S,k,m,P=Pe(()=>{"use strict";u=async e=>de.readFile(e,"utf-8"),f=async(e,r)=>{await de.mkdir(Tr.dirname(e),{recursive:!0}),await de.writeFile(e,r,"utf-8")},_=async(e,r)=>{await de.mkdir(Tr.dirname(r),{recursive:!0}),await de.copyFile(e,r)},T=async e=>{wt(e)&&await de.unlink(e)},S=async e=>{wt(e)&&await de.rm(e,{recursive:!0,force:!0})},k=async(e,r)=>{let t=await u(e),n=JSON.parse(t),i=r(n);await f(e,JSON.stringify(i,null,2))},m=e=>wt(e)});var Nr={};St(Nr,{PROJECT_PATHS:()=>c});var c,w=Pe(()=>{"use strict";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",MIDDLEWARE:"src/middleware.ts",PROXY:"src/proxy.ts",I18N_TYPES:"src/types/i18n.ts",HTTP_UTILS:"src/lib/utils/http",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",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",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 Or={};St(Or,{cleanupHttpTypes:()=>ve,isFileUsed:()=>Mi,isStringUsed:()=>Gi,migrateClientUsages:()=>It,removeHttpExports:()=>At,updateConfigIndex:()=>Nt,updateHttpIndex:()=>Rt,updateTypesIndex:()=>bt,updateUtilsIndex:()=>$t});import{exec as Di}from"node:child_process";import F from"node:path";import{promisify as Li}from"node:util";var Et,Rt,$t,bt,Nt,ve,It,At,Mi,Gi,it=Pe(()=>{"use strict";w();P();Et=Li(Di),Rt=async(e,r)=>{let t=F.join(e,c.HTTP_UTILS,"index.ts");if(m(t)){let n=await u(t);n=n.replace(/export .* from '\.\/axios-client';?\n?/g,""),n=n.replace(/export .* from '\.\/fetch-client';?\n?/g,""),r.includes("fetch")&&!n.includes("fetch-client")&&(n+=`export * from './fetch-client';
|
|
3
|
+
`),r.includes("axios")&&!n.includes("axios-client")&&(n+=`export * from './axios-client';
|
|
4
|
+
`),await f(t,n)}},$t=async e=>{let r=F.join(e,c.UTILS_INDEX);if(m(r)){let t=await u(r);t.includes("./http")||(t+=`export * from './http';
|
|
5
|
+
`,await f(r,t))}},bt=async e=>{let r=F.join(e,c.TYPES_INDEX);if(m(r)){let t=await u(r);t.includes("./common")||(t+=`export * from './common';
|
|
6
6
|
`),t.includes("./utility")||(t+=`export * from './utility';
|
|
7
|
-
`),await f(r,t)}}
|
|
8
|
-
`,await f(r,t))}},ve=async(e,r)=>{let t=F.join(e,c.HTTP_TYPES);if(m(t)){let
|
|
7
|
+
`),await f(r,t)}},Nt=async e=>{let r=F.join(e,c.CONFIG_INDEX);if(m(r)){let t=await u(r);t.includes("./app-apis")||(t+=`export * from './app-apis';
|
|
8
|
+
`,await f(r,t))}},ve=async(e,r)=>{let t=F.join(e,c.HTTP_TYPES);if(m(t)){let n=await u(t);r.includes("axios")||(n=n.replace(/declare module 'axios'\s*\{\s*export interface AxiosRequestConfig\s*\{[\s\S]*?\}\s*\}/g,""),n=`${n.replace(/\n\s*\n/g,`
|
|
9
9
|
|
|
10
10
|
`).trim()}
|
|
11
|
-
`,await f(t,
|
|
12
|
-
`).filter(
|
|
13
|
-
`).filter(
|
|
14
|
-
`).filter(u=>{let[g]=u.split(":"),x=F.resolve(e,g);return!t.some(y=>!!(x===y||x.startsWith(y+F.sep)))}).length>0:!1}catch(s){return console.warn(`Warning: Failed to check usage for string ${r}:`,s),!0}}});import{existsSync as as}from"node:fs";import{access as cs}from"node:fs/promises";import qr from"node:path";var ne,nt,Wr=Pe(()=>{"use strict";ne=async(e,r,t)=>{try{return await cs(qr.join(e,t,r)),!0}catch{return!1}},nt=(e,r,t)=>as(qr.join(e,r,t))});import{access as kt,readFile as ps}from"node:fs/promises";import st from"node:path";var I,Zr=Pe(()=>{"use strict";I=async e=>{try{let r=st.join(e,"package.json"),t=await ps(r,"utf-8"),o=JSON.parse(t),n={...o.dependencies,...o.devDependencies},i=!!(n["@reduxjs/toolkit"]&&n["react-redux"]),s=!!n["next-intl"],a=!!n.axios,p=!1,u=st.join(e,"src","lib","utils","http","axios-client");try{await kt(u)}catch{a=!1}let g=st.join(e,"src","lib","utils","http","fetch-client");try{await kt(g),p=!0}catch{p=!1}let x;a&&p?x="both":a?x="axios":p?x="fetch":x="none";let y=!!n.vitest;if(!y)try{await kt(st.join(e,"vitest.config.ts")),y=!0}catch{}return{hasRedux:i,httpClient:x,hasI18n:s,hasTests:y}}catch(r){throw new Error(`Failed to detect project setup: ${r}`,{cause:r})}}});var $e={};Pt($e,{detectProjectSetup:()=>I,directoryExists:()=>ne,fileExistsAt:()=>nt});var L=Pe(()=>{"use strict";Wr();Zr()});import{Command as jc}from"commander";import At from"node:path";import ie from"picocolors";function Pr(e){let{logger:r=s=>console.error(s),exitOnCancel:t=process.env.NODE_ENV!=="test"}=e??{},o=()=>{console.log(""),console.log(""),r("Setup cancelled by user"),console.log(""),t&&process.exit(0)},n=s=>{if(s!==null&&typeof s=="object"&&"code"in s&&s.code==="ERR_USE_AFTER_CLOSE"){o();return}if(s instanceof Error){r(`Uncaught exception: ${s.message}`);return}r(`Uncaught exception: ${String(s)}`)},i=s=>{if(s instanceof Error){r(`Unhandled promise rejection: ${s.message}`);return}r(`Unhandled promise rejection: ${String(s)}`)};return process.on("SIGINT",o),process.on("SIGTERM",o),process.on("uncaughtException",n),process.on("unhandledRejection",i),()=>{process.off("SIGINT",o),process.off("SIGTERM",o),process.off("uncaughtException",n),process.off("unhandledRejection",i)}}import K from"picocolors";var cn={reset:e=>e,red:K.red,green:K.green,yellow:K.yellow,blue:K.blue,cyan:K.cyan,magenta:K.magenta,white:K.white,gray:K.gray,bright:K.bold,dim:K.dim};function me(e,r="reset"){let t=cn[r]??(o=>o);console.log(t(e))}function tt(e){me(`\u2716 ${e}`,"red")}var v=tt;function l(e){me(e)}function vr(){console.log(""),me("\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"),me("\u2551 \u2551","cyan"),me("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),me("\u2551 \u2551","cyan"),me("\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 pn from"ora";function R(e="",r){let t=pn({text:e,...r});return t.start(),t}var ln=e=>e.charAt(0).toUpperCase()+e.slice(1),A=e=>e.replace(/-([a-z])/g,(r,t)=>t.toUpperCase()),$=e=>ln(A(e));import mn from"ora";var h=mn();P();import{exec as dn}from"node:child_process";import{promisify as un}from"node:util";var Z=un(dn),wr=async(e,r,t)=>{try{if(await Z("git init",{cwd:e}),await Z("git add .",{cwd:e}),await Z('git commit -m "Initial commit from @teispace/next-maker"',{cwd:e}),r&&(await Z(`git remote add origin ${r}`,{cwd:e}),t)){await Z("git fetch origin",{cwd:e});let o="main";try{await Z("git show-branch origin/main",{cwd:e})}catch{try{await Z("git show-branch origin/master",{cwd:e}),o="master"}catch{o="main"}}await Z(`git merge origin/${o} --allow-unrelated-histories -X ours`,{cwd:e}),await Z(`git push origin HEAD:${o}`,{cwd:e})}}catch(o){console.warn("Failed to initialize git repository",o)}};import{exec as fn}from"node:child_process";import{promisify as gn}from"node:util";var Ee=gn(fn),Cr=async(e,r)=>{let t=`${r} install`;try{await Ee(t,{cwd:e})}catch(o){throw new Error(`Failed to install dependencies with ${r}: ${o}`,{cause:o})}},U=async(e,r,t)=>{let o=r==="npm"?`${r} run ${t}`:`${r} ${t}`;try{await Ee(o,{cwd:e})}catch(n){console.warn(`Warning: Failed to run script '${t}': ${n}`)}},hn=()=>{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"},O=async e=>{let{existsSync:r}=await import("node:fs"),t=await import("node:path");return r(t.join(e,"pnpm-lock.yaml"))?"pnpm":r(t.join(e,"yarn.lock"))?"yarn":r(t.join(e,"bun.lockb"))?"bun":r(t.join(e,"package-lock.json"))?"npm":hn()},xn=async(e,r,t)=>{if(t.length===0)return;let n=`${Tr(r)} ${t.join(" ")}`;try{await Ee(n,{cwd:e})}catch(i){throw new Error(`Failed to install packages with ${r}: ${i}`,{cause:i})}},Tr=e=>{switch(e){case"npm":return"npm install";case"yarn":return"yarn add";case"pnpm":return"pnpm add";case"bun":return"bun add";default:return"npm install"}},z=async(e,r)=>{let t=await O(e);await xn(e,t,[r])},yn=e=>{switch(e){case"npm":return"--save-dev";case"yarn":case"pnpm":case"bun":return"-D";default:return"--save-dev"}},ue=async(e,r,t)=>{if(t.length===0)return;let o=Tr(r),n=yn(r),i=`${o} ${n} ${t.join(" ")}`;try{await Ee(i,{cwd:e})}catch(s){throw new Error(`Failed to install dev packages with ${r}: ${s}`,{cause:s})}},rt=async(e,r)=>{let t=await O(e);await ue(e,t,[r])},St=async(e,r,t)=>{if(t.length===0)return;let n=`${Pn(r)} ${t.join(" ")}`;try{await Ee(n,{cwd:e})}catch(i){throw new Error(`Failed to uninstall packages with ${r}: ${i}`,{cause:i})}},Pn=e=>{switch(e){case"npm":return"npm uninstall";case"yarn":return"yarn remove";case"pnpm":return"pnpm remove";case"bun":return"bun remove";default:return"npm uninstall"}},wt=async(e,r)=>{let t=await O(e);await St(e,t,[r])};import vn from"enquirer";var{prompt:Sn}=vn,Er=async e=>{let r=await Sn([{type:"input",name:"projectName",message:"What is the project name?",initial:e||"my-app",skip:!!e,validate:t=>/^[a-z0-9-_]+$/.test(t)?!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:t=>/^\d+\.\d+\.\d+$/.test(t)?!0:"Version must be a valid semantic version (x.y.z)."},{type:"input",name:"email",message:"Support email:",initial:"support@example.com",validate:t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)?!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:t=>t?/^https:\/\/github\.com\/[\w-]+\/[\w.-]+\/issues$/.test(t)?!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:t=>t?/^https:\/\/github\.com\/[\w-]+\/[\w.-]+#readme$/.test(t)?!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 t=this.state?.answers??this.enquirer?.answers??{};return!!(t.httpClient&&t.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:"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:t=>/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(t)?!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:t=>/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(t)?!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:t=>/^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$/.test(t)?!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(r.company=r.author,r.gitRemote&&!r.gitHomepage){let t=r.gitRemote.replace("git@github.com:","https://github.com/").replace(/\.git$/,"");r.gitHomepage=`${t}#readme`,r.gitIssues=`${t}/issues`}return r};P();import wn from"node:path";var re=async(e,r)=>{let t=Cn(r);await f(wn.join(e,"test/test-utils.tsx"),t)},Cn=({redux:e,i18n:r})=>e&&r?Tn:e?En:r?Rn:$n,Tn=`import { type RenderOptions, render } from '@testing-library/react';
|
|
11
|
+
`,await f(t,n))}},It=async(e,r,t)=>{if(r===t)return;let n=r==="fetch"?"fetchClient":"axiosClient",i=t==="fetch"?"fetchClient":"axiosClient",o=r==="fetch"?"fetch-client":"axios-client",s=t==="fetch"?"fetch-client":"axios-client",a=F.join(e,"src");try{let p=`grep -rl "${n}" "${a}" --include="*.ts" --include="*.tsx" || true`,{stdout:d}=await Et(p);if(!d.trim())return;let g=d.trim().split(`
|
|
12
|
+
`).filter(h=>h);for(let h of g){if(!m(h))continue;let y=await u(h),Y=!1,vt=new RegExp(`(import\\s*\\{[^}]*?)\\b${n}\\b([^}]*?\\}\\s*from\\s*['"][^'"]*?/http['"])`,"g");vt.test(y)&&(y=y.replace(vt,`$1${i}$2`),Y=!0);let tt=new RegExp(`import\\s+${n}\\s+from\\s+['"]([^'"]*?)/${o}['"]`,"g");tt.test(y)&&(y=y.replace(tt,`import ${i} from '$1/${s}'`),Y=!0);let rt=new RegExp(`\\b${n}\\b(?=\\.)`,"g");rt.test(y)&&(y=y.replace(rt,i),Y=!0);let Sr=new RegExp(`\\b${n}\\b(?![.\\w])`,"g");Sr.test(y)&&(y=y.replace(Sr,i),Y=!0),Y&&await f(h,y)}}catch(p){console.warn("Warning: Could not scan for HTTP client usages:",p)}},At=async e=>{let r=F.join(e,c.UTILS_INDEX);if(m(r)){let n=await u(r);n=n.replace(/^\s*export\s+\*\s+from\s+['"]\.\/http['"];?.*$/gm,""),await f(r,n)}let t=F.join(e,c.TYPES_INDEX);if(m(t)){let n=await u(t),i=F.join(e,c.COMMON_TYPES_DIR);if(!m(i))n=n.replace(/^\s*export\s+\*\s+from\s+['"]\.\/common['"];?.*$/gm,"");else{let o=F.join(i,"index.ts");if(m(o)){let s=await u(o);s=s.replace(/^\s*export\s+\*\s+from\s+['"]\.\/http\.types['"];?.*$/gm,""),await f(o,s)}}m(F.join(e,c.UTILITY_TYPES_DIR))||(n=n.replace(/^\s*export\s+\*\s+from\s+['"]\.\/utility['"];?.*$/gm,"")),await f(t,n)}},Mi=async(e,r,t=[])=>{let n=F.join(e,"src"),o=`grep -r "from ['\\"].*${r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}.*['\\"]" "${n}" --include="*.ts" --include="*.tsx" || true`;try{let{stdout:s}=await Et(o);return s.trim()?s.trim().split(`
|
|
13
|
+
`).filter(d=>{let[g]=d.split(":"),h=F.resolve(e,g);return!t.some(y=>!!(h===y||h.startsWith(y+F.sep)))}).length>0:!1}catch(s){return console.warn(`Warning: Failed to check usage for ${r}:`,s),!0}},Gi=async(e,r,t=[])=>{let n=F.join(e,"src"),o=`grep -r "${r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}" "${n}" --include="*.ts" --include="*.tsx" || true`;try{let{stdout:s}=await Et(o);return s.trim()?s.trim().split(`
|
|
14
|
+
`).filter(d=>{let[g]=d.split(":"),h=F.resolve(e,g);return!t.some(y=>!!(h===y||h.startsWith(y+F.sep)))}).length>0:!1}catch(s){return console.warn(`Warning: Failed to check usage for string ${r}:`,s),!0}}});import{existsSync as ds}from"node:fs";import{access as us}from"node:fs/promises";import Qr from"node:path";var ie,at,en=Pe(()=>{"use strict";ie=async(e,r,t)=>{try{return await us(Qr.join(e,t,r)),!0}catch{return!1}},at=(e,r,t)=>ds(Qr.join(e,r,t))});import{access as _t,readFile as fs}from"node:fs/promises";import ct from"node:path";var I,tn=Pe(()=>{"use strict";I=async e=>{try{let r=ct.join(e,"package.json"),t=await fs(r,"utf-8"),n=JSON.parse(t),i={...n.dependencies,...n.devDependencies},o=!!(i["@reduxjs/toolkit"]&&i["react-redux"]),s=!!i["next-intl"],a=!!i.axios,p=!1,d=ct.join(e,"src","lib","utils","http","axios-client");try{await _t(d)}catch{a=!1}let g=ct.join(e,"src","lib","utils","http","fetch-client");try{await _t(g),p=!0}catch{p=!1}let h;a&&p?h="both":a?h="axios":p?h="fetch":h="none";let y=!!i.vitest;if(!y)try{await _t(ct.join(e,"vitest.config.ts")),y=!0}catch{}return{hasRedux:o,httpClient:h,hasI18n:s,hasTests:y}}catch(r){throw new Error(`Failed to detect project setup: ${r}`,{cause:r})}}});var Ne={};St(Ne,{detectProjectSetup:()=>I,directoryExists:()=>ie,fileExistsAt:()=>at});var L=Pe(()=>{"use strict";en();tn()});import{Command as Hc}from"commander";import Ot from"node:path";import oe from"picocolors";function wr(e){let{logger:r=s=>console.error(s),exitOnCancel:t=process.env.NODE_ENV!=="test"}=e??{},n=()=>{console.log(""),console.log(""),r("Setup cancelled by user"),console.log(""),t&&process.exit(0)},i=s=>{if(s!==null&&typeof s=="object"&&"code"in s&&s.code==="ERR_USE_AFTER_CLOSE"){n();return}if(s instanceof Error){r(`Uncaught exception: ${s.message}`);return}r(`Uncaught exception: ${String(s)}`)},o=s=>{if(s instanceof Error){r(`Unhandled promise rejection: ${s.message}`);return}r(`Unhandled promise rejection: ${String(s)}`)};return process.on("SIGINT",n),process.on("SIGTERM",n),process.on("uncaughtException",i),process.on("unhandledRejection",o),()=>{process.off("SIGINT",n),process.off("SIGTERM",n),process.off("uncaughtException",i),process.off("unhandledRejection",o)}}import K from"picocolors";var ui={reset:e=>e,red:K.red,green:K.green,yellow:K.yellow,blue:K.blue,cyan:K.cyan,magenta:K.magenta,white:K.white,gray:K.gray,bright:K.bold,dim:K.dim};function me(e,r="reset"){let t=ui[r]??(n=>n);console.log(t(e))}function nt(e){me(`\u2716 ${e}`,"red")}var v=nt;function l(e){me(e)}function Cr(){console.log(""),me("\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"),me("\u2551 \u2551","cyan"),me("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),me("\u2551 \u2551","cyan"),me("\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 fi from"ora";function R(e="",r){let t=fi({text:e,...r});return t.start(),t}var gi=e=>e.charAt(0).toUpperCase()+e.slice(1),A=e=>e.replace(/-([a-z])/g,(r,t)=>t.toUpperCase()),$=e=>gi(A(e));import hi from"ora";var x=hi();P();import{exec as xi}from"node:child_process";import{promisify as yi}from"node:util";var Z=yi(xi),Er=async(e,r,t)=>{try{if(await Z("git init",{cwd:e}),await Z("git add .",{cwd:e}),await Z('git commit -m "Initial commit from @teispace/next-maker"',{cwd:e}),r&&(await Z(`git remote add origin ${r}`,{cwd:e}),t)){await Z("git fetch origin",{cwd:e});let n="main";try{await Z("git show-branch origin/main",{cwd:e})}catch{try{await Z("git show-branch origin/master",{cwd:e}),n="master"}catch{n="main"}}await Z(`git merge origin/${n} --allow-unrelated-histories -X ours`,{cwd:e}),await Z(`git push origin HEAD:${n}`,{cwd:e})}}catch(n){console.warn("Failed to initialize git repository",n)}};import{exec as Pi}from"node:child_process";import{promisify as vi}from"node:util";var Re=vi(Pi),Rr=async(e,r)=>{let t=`${r} install`;try{await Re(t,{cwd:e})}catch(n){throw new Error(`Failed to install dependencies with ${r}: ${n}`,{cause:n})}},U=async(e,r,t)=>{let n=r==="npm"?`${r} run ${t}`:`${r} ${t}`;try{await Re(n,{cwd:e})}catch(i){console.warn(`Warning: Failed to run script '${t}': ${i}`)}},Si=()=>{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"},O=async e=>{let{existsSync:r}=await import("node:fs"),t=await import("node:path");return r(t.join(e,"pnpm-lock.yaml"))?"pnpm":r(t.join(e,"yarn.lock"))?"yarn":r(t.join(e,"bun.lockb"))?"bun":r(t.join(e,"package-lock.json"))?"npm":Si()},wi=async(e,r,t)=>{if(t.length===0)return;let i=`${$r(r)} ${t.join(" ")}`;try{await Re(i,{cwd:e})}catch(o){throw new Error(`Failed to install packages with ${r}: ${o}`,{cause:o})}},$r=e=>{switch(e){case"npm":return"npm install";case"yarn":return"yarn add";case"pnpm":return"pnpm add";case"bun":return"bun add";default:return"npm install"}},z=async(e,r)=>{let t=await O(e);await wi(e,t,[r])},Ci=e=>{switch(e){case"npm":return"--save-dev";case"yarn":case"pnpm":case"bun":return"-D";default:return"--save-dev"}},ue=async(e,r,t)=>{if(t.length===0)return;let n=$r(r),i=Ci(r),o=`${n} ${i} ${t.join(" ")}`;try{await Re(o,{cwd:e})}catch(s){throw new Error(`Failed to install dev packages with ${r}: ${s}`,{cause:s})}},ot=async(e,r)=>{let t=await O(e);await ue(e,t,[r])},Ct=async(e,r,t)=>{if(t.length===0)return;let i=`${Ti(r)} ${t.join(" ")}`;try{await Re(i,{cwd:e})}catch(o){throw new Error(`Failed to uninstall packages with ${r}: ${o}`,{cause:o})}},Ti=e=>{switch(e){case"npm":return"npm uninstall";case"yarn":return"yarn remove";case"pnpm":return"pnpm remove";case"bun":return"bun remove";default:return"npm uninstall"}},Tt=async(e,r)=>{let t=await O(e);await Ct(e,t,[r])};import Ei from"enquirer";var{prompt:Ri}=Ei,br=async e=>{let r=await Ri([{type:"input",name:"projectName",message:"What is the project name?",initial:e||"my-app",skip:!!e,validate:t=>/^[a-z0-9-_]+$/.test(t)?!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:t=>/^\d+\.\d+\.\d+$/.test(t)?!0:"Version must be a valid semantic version (x.y.z)."},{type:"input",name:"email",message:"Support email:",initial:"support@example.com",validate:t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)?!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:t=>t?/^https:\/\/github\.com\/[\w-]+\/[\w.-]+\/issues$/.test(t)?!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:t=>t?/^https:\/\/github\.com\/[\w-]+\/[\w.-]+#readme$/.test(t)?!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 t=this.state?.answers??this.enquirer?.answers??{};return!!(t.httpClient&&t.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:"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:t=>/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(t)?!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:t=>/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(t)?!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:t=>/^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$/.test(t)?!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(r.company=r.author,r.gitRemote&&!r.gitHomepage){let t=r.gitRemote.replace("git@github.com:","https://github.com/").replace(/\.git$/,"");r.gitHomepage=`${t}#readme`,r.gitIssues=`${t}/issues`}return r};P();import $i from"node:path";var re=async(e,r)=>{let t=bi(r);await f($i.join(e,"test/test-utils.tsx"),t)},bi=({redux:e,i18n:r})=>e&&r?Ni:e?Ii:r?Ai:ki,Ni=`import { type RenderOptions, render } from '@testing-library/react';
|
|
15
15
|
import { NextIntlClientProvider } from 'next-intl';
|
|
16
16
|
import type { ReactElement, ReactNode } from 'react';
|
|
17
17
|
import { Provider } from 'react-redux';
|
|
@@ -72,7 +72,7 @@ export function renderWithProviders(
|
|
|
72
72
|
|
|
73
73
|
export * from '@testing-library/react';
|
|
74
74
|
export { default as userEvent } from '@testing-library/user-event';
|
|
75
|
-
`,
|
|
75
|
+
`,Ii=`import { type RenderOptions, render } from '@testing-library/react';
|
|
76
76
|
import type { ReactElement, ReactNode } from 'react';
|
|
77
77
|
import { Provider } from 'react-redux';
|
|
78
78
|
import { type AppState, type AppStore, makeStore } from '@/store';
|
|
@@ -111,7 +111,7 @@ export function renderWithProviders(
|
|
|
111
111
|
|
|
112
112
|
export * from '@testing-library/react';
|
|
113
113
|
export { default as userEvent } from '@testing-library/user-event';
|
|
114
|
-
`,
|
|
114
|
+
`,Ai=`import { type RenderOptions, render } from '@testing-library/react';
|
|
115
115
|
import { NextIntlClientProvider } from 'next-intl';
|
|
116
116
|
import type { ReactElement, ReactNode } from 'react';
|
|
117
117
|
import type { SupportedLocale } from '@/types/i18n';
|
|
@@ -151,7 +151,7 @@ export function renderWithProviders(
|
|
|
151
151
|
|
|
152
152
|
export * from '@testing-library/react';
|
|
153
153
|
export { default as userEvent } from '@testing-library/user-event';
|
|
154
|
-
|
|
154
|
+
`,ki=`import { type RenderOptions, render } from '@testing-library/react';
|
|
155
155
|
import type { ReactElement, ReactNode } from 'react';
|
|
156
156
|
|
|
157
157
|
type TestProvidersProps = {
|
|
@@ -173,9 +173,9 @@ export function renderWithProviders(ui: ReactElement, options: ExtendedRenderOpt
|
|
|
173
173
|
|
|
174
174
|
export * from '@testing-library/react';
|
|
175
175
|
export { default as userEvent } from '@testing-library/user-event';
|
|
176
|
-
`;import
|
|
177
|
-
`),await T(b.join(e,c.CLIENT_UTILS)),await ve(e,[])):(await S(r),await
|
|
178
|
-
`};
|
|
176
|
+
`;import Ir from"node:path";var C={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"};w();P();var Ar=async(e,r)=>{if(r.bundleAnalyzer)return;let t=Ir.join(e,c.NEXT_CONFIG);if(m(t)){let n=await u(t);n=n.replace(/import\s+withBundleAnalyzer\s+from\s+'@next\/bundle-analyzer';\n/,""),n=n.replace(/const\s+bundleAnalyzer\s*=\s*withBundleAnalyzer\(\{[\s\S]*?\}\);\n+/,""),n=n.replace(/bundleAnalyzer\(([^)]*)\)/,"$1"),await f(t,n)}await k(Ir.join(e,c.PACKAGE_JSON),n=>(n.devDependencies&&delete n.devDependencies[C.NEXT_BUNDLE_ANALYZER],n.scripts&&delete n.scripts.analyze,n))};w();P();import $e from"node:path";var kr=async(e,r)=>{r.darkMode||(await T($e.join(e,c.THEME_PROVIDER)),await Oi(e),await _i(e),await ji(e))},Oi=async e=>{let r=$e.join(e,c.PROVIDERS_INDEX),t=await u(r);t=t.replace(/export \* from '\.\/CustomThemeProvider';\n/,""),await f(r,t)},_i=async e=>{let r=$e.join(e,c.GLOBALS_CSS);if(m(r)){let t=await u(r);t=t.replace(/@custom-variant dark \(.*?\);\n\n/,""),t=t.replace(/@theme \{[\s\S]*?\}\n/,""),await f(r,t)}},Fi=e=>e.replace(/\bbg-light\b\s?/g,"").replace(/\bdark:bg-dark\b\s?/g,"").replace(/\s{2,}/g," ").replace(/suppressHydrationWarning=\{true\}/g,""),ji=async e=>{let r=[$e.join(e,c.ROOT_LAYOUT),$e.join(e,"src/app/[locale]/layout.tsx")];for(let t of r)if(m(t)){let n=await u(t),i=Fi(n);i!==n&&await f(t,i)}};w();P();it();import b from"node:path";var _r=async(e,r)=>{let t=b.join(e,c.HTTP_UTILS),n=r.httpClient!=="none"||!!r.reactSecureStorage;r.httpClient==="none"?await Hi(e,t,n):r.httpClient==="axios"?await Ui(e,t):r.httpClient==="fetch"&&await zi(e,t)},Hi=async(e,r,t)=>{await S(b.join(e,c.AXIOS_CLIENT)),await S(b.join(e,c.FETCH_CLIENT)),t?(await f(b.join(r,"index.ts"),`export * from './token-store';
|
|
177
|
+
`),await T(b.join(e,c.CLIENT_UTILS)),await ve(e,[])):(await S(r),await Vi(e),await T(b.join(e,c.APP_APIS)),await Ji(e)),await Bi(e),await S(b.join(e,c.ERRORS_DIR)),t||(await S(b.join(e,c.COMMON_TYPES_DIR)),await S(b.join(e,c.UTILITY_TYPES_DIR)),await Xi(e))},Ui=async(e,r)=>{await S(b.join(e,c.FETCH_CLIENT));let t=await u(b.join(r,"index.ts"));t=t.replace(/export .* from '\.\/fetch-client';\n/g,""),await f(b.join(r,"index.ts"),t);let n=b.join(e,c.HTTP_TYPES);if(m(n)){let i=await u(n);i=i.replace(/export interface FetchClientOptions \{[\s\S]*?\}\n\n/,""),i=i.replace(/export interface ExtendedRequestInit extends RequestInit \{[\s\S]*?\}\n\n/,""),await f(n,i)}},zi=async(e,r)=>{await S(b.join(e,c.AXIOS_CLIENT));let t=await u(b.join(r,"index.ts"));t=t.replace(/export .* from '\.\/axios-client';\n/g,""),await f(b.join(r,"index.ts"),t),await ve(e,["fetch"]);let n=b.join(e,c.HTTP_TYPES);if(m(n)){let i=await u(n);i=i.replace(/export interface AxiosClientOptions \{[\s\S]*?\}\n\n/,""),await f(n,i)}},Vi=async e=>{let r=b.join(e,c.UTILS_INDEX);if(m(r)){let t=await u(r);t=t.replace(/export \* from '\.\/http';\n/,""),await f(r,t)}},Ji=async e=>{let r=b.join(e,c.CONFIG_INDEX);if(m(r)){let t=await u(r);t=t.replace(/export \* from '\.\/app-apis';\n/,""),await f(r,t)}},Bi=async e=>{let r=b.join(e,c.CONSTANTS);if(m(r)){let t=await u(r);t=t.replace(/export const API_RESPONSE_DATA_KEY = 'data';\n/,""),t=t.replace(/export const SAVE_AUTH_TOKENS = false;\n/,""),await f(r,t)}},Xi=async e=>{let r=b.join(e,c.TYPES_INDEX);if(m(r)){let t=await u(r);t=t.replace(/export \* from '\.\/utility';\n/,""),t=t.replace(/export \* from '\.\/common';\n/,""),await f(r,t)}};w();P();import H from"node:path";var Fr=async(e,r)=>{r.i18n||(await Ki(e),await qi(e),await Wi(e),await Zi(e),await Yi(e))},Yi=async e=>{let r=H.join(e,c.ENV_EXAMPLE);if(!m(r))return;let t=await u(r);t=t.replace(/#[^\n]*IANA time zone[\s\S]*?DEFAULT_TIMEZONE=[^\n]*\n?/,""),t=t.replace(/#[^\n]*Fallback locale[\s\S]*?DEFAULT_LOCALE=[^\n]*\n?/,""),t=t.replace(/^DEFAULT_TIMEZONE=.*\n?/m,""),t=t.replace(/^DEFAULT_LOCALE=.*\n?/m,""),await f(r,t)},Ki=async e=>{await S(H.join(e,c.I18N_DIR)),await S(H.join(e,c.LOCALE_DIR)),await T(H.join(e,c.PROXY)),await T(H.join(e,c.MIDDLEWARE)),await T(H.join(e,c.I18N_TYPES)),await T(H.join(e,c.APP_LOCALES))},qi=async e=>{let r=H.join(e,c.TYPES_INDEX);if(m(r)){let n=await u(r);n=n.replace(/export \* from '\.\/i18n';\n/,""),await f(r,n)}let t=H.join(e,c.CONFIG_INDEX);if(m(t)){let n=await u(t);n=n.replace(/export \* from '\.\/app-locales';\n/,""),await f(t,n)}},Wi=async e=>{let r=H.join(e,c.NEXT_CONFIG);if(m(r)){let t=await u(r);t=t.replace(/import createNextIntlPlugin from 'next-intl\/plugin';\n/,""),t=t.replace(/const withNextIntl = createNextIntlPlugin\(\);\n+/,""),t=t.replace(/withNextIntl\(([^)]+)\)/,"$1"),await f(r,t)}},Zi=async e=>{let r=H.join(e,c.COUNTER_COMPONENT);if(m(r)){let i=await u(r);i=i.replace(/import\s+\{\s*useTranslations\s*\}\s+from\s+['"]next-intl['"];\n/,""),i=i.replace(/\s*const\s+t\s*=\s*useTranslations\(['"]Count['"]\);\n/,""),i=i.replace(/\{t\('currentCount',\s*\{\s*count:\s*value\s*\}\)\}/g,"Current Count: {value}"),i=i.replace(/\{t\(['"]increment['"]\)\}/g,"Increment"),i=i.replace(/\{t\(['"]decrement['"]\)\}/g,"Decrement"),i=i.replace(/\{t\(['"]reset['"]\)\}/g,"Reset"),await f(r,i)}let t=H.join(e,"src/features/counter/components/Counter.test.tsx"),n=H.join(e,"test/test-utils.tsx");await T(t),await T(n)};w();P();import ne from"node:path";var jr=async(e,r)=>{r.httpClient!=="none"||r.reactSecureStorage||await S(ne.join(e,c.STORAGE_SERVICE))},Dr=async e=>{await T(ne.join(e,c.LICENSE))},Lr=async e=>{await T(ne.join(e,c.CHANGELOG))},Mr=async e=>{await T(ne.join(e,c.NVM_RC)),await T(ne.join(e,c.NPM_RC))},Gr=async e=>{await S(ne.join(e,c.CLAUDE_DIR)),await S(ne.join(e,c.DOCS_DIR)),await S(ne.join(e,c.VSCODE_DIR))};import Hr from"node:path";w();P();var Ur=async(e,r)=>{if(r.reactCompiler)return;let t=Hr.join(e,c.NEXT_CONFIG);if(m(t)){let n=await u(t);n=n.replace(/^\s*reactCompiler:\s*true,?\n/m,""),await f(t,n)}await k(Hr.join(e,c.PACKAGE_JSON),n=>(n.devDependencies&&delete n.devDependencies[C.REACT_COMPILER_BABEL],n))};w();P();import fe from"node:path";var zr=async(e,r)=>{r.redux||(await S(fe.join(e,c.STORE)),await S(fe.join(e,c.COUNTER_FEATURE)),await T(fe.join(e,c.STORE_PROVIDER)),await T(fe.join(e,"test/test-utils.tsx")),await Qi(e),await es(e))},Qi=async e=>{let r=fe.join(e,c.PROVIDERS_INDEX),t=await u(r);t=t.replace(/export \* from '\.\/StoreProvider';\n/,""),await f(r,t)},es=async e=>{let r=[fe.join(e,c.ROOT_PAGE),fe.join(e,c.LOCALE_PAGE)];for(let t of r)if(m(t)){let n=await u(t);n=n.replace(/import\s+\{\s*Counter\s*\}\s+from\s+['"]@\/features\/counter['"];\n?/,""),n=n.replace(/<Counter\s*\/>\n?/g,""),await f(t,n)}};import st from"node:path";w();P();var ts=[C.VITEST,C.JSDOM,C.VITE_PLUGIN_REACT,C.TESTING_LIBRARY_DOM,C.TESTING_LIBRARY_JEST_DOM,C.TESTING_LIBRARY_REACT,C.TESTING_LIBRARY_USER_EVENT],Vr=async(e,r)=>{r.tests||(await T(st.join(e,c.VITEST_CONFIG)),await S(st.join(e,c.TEST_DIR)),await T(st.join(e,"src/features/counter/components/Counter.test.tsx")),await k(st.join(e,c.PACKAGE_JSON),t=>{if(t.devDependencies)for(let n of ts)delete t.devDependencies[n];return t.scripts&&(delete t.scripts.test,delete t.scripts["test:watch"],delete t.scripts["test:coverage"],typeof t.scripts.validate=="string"&&(t.scripts.validate=t.scripts.validate.replace(/\s*&&\s*yarn\s+test(?![:\w])/,"").replace(/\byarn\s+test(?![:\w])\s*&&\s*/,""))),t}))};var Jr=async(e,r)=>{let t=R("Customizing features...");try{await _r(e,r),await jr(e,r),await zr(e,r),await kr(e,r),await Ar(e,r),await Fr(e,r),await Ur(e,r),await Vr(e,r),await Dr(e),await Lr(e),await Mr(e),await Gr(e),r.tests&&await re(e,{redux:r.redux,i18n:r.i18n}),t.succeed("Features customized.")}catch(n){throw t.fail("Failed to customize features."),n}};import rs from"node:path";w();P();var Br=async(e,r)=>{let t=R("Configuring package.json...");try{let n=r.gitRemote,i=r.gitHomepage,o=r.gitIssues;r.gitRemote&&(r.gitRemote.startsWith("git@github.com:")&&(n=r.gitRemote.replace("git@github.com:","https://github.com/").replace(/\.git$/,"")),i||(i=`${n.replace(/\.git$/,"")}#readme`),o||(o=`${n.replace(/\.git$/,"")}/issues`),n.endsWith(".git")||(n=`${n}.git`)),await k(rs.join(e,c.PACKAGE_JSON),s=>(s.name=r.projectName,s.version=r.version,s.description=r.description,s.author=r.author,delete s.packageManager,r.gitRemote?(i&&(s.homepage=i),o&&(s.bugs={url:o}),n&&(s.repository={type:"git",url:n})):(delete s.homepage,delete s.bugs,delete s.repository),r.redux||(delete s.dependencies[C.REDUX_TOOLKIT],delete s.dependencies[C.REACT_REDUX],delete s.dependencies[C.REDUX],delete s.dependencies[C.REDUX_PERSIST]),r.httpClient!=="none"||r.reactSecureStorage||delete s.dependencies[C.REACT_SECURE_STORAGE],r.i18n||delete s.dependencies[C.NEXT_INTL],r.darkMode||delete s.dependencies[C.NEXT_THEMES],r.httpClient==="none"?delete s.dependencies[C.AXIOS]:r.httpClient==="fetch"&&delete s.dependencies[C.AXIOS],s)),t.succeed("package.json configured.")}catch(n){throw t.fail("Failed to configure package.json."),n}};import{readdir as Xr}from"node:fs/promises";import N from"node:path";w();P();var Yr=async(e,r)=>{await ns(e,r),await os(e,r),await is(e,r),await ss(e,r),await as(e,r),await cs(e,r),await ps(e,r)},ns=async(e,r)=>{r.preCommitHooks||(await S(N.join(e,c.HUSKY_DIR)),await T(N.join(e,c.COMMITLINT_CONFIG)),await T(N.join(e,c.LINTSTAGED_RC)),await k(N.join(e,c.PACKAGE_JSON),t=>(delete t.devDependencies[C.HUSKY],delete t.devDependencies[C.COMMITLINT_CLI],delete t.devDependencies[C.COMMITLINT_CONFIG],delete t.devDependencies[C.LINT_STAGED],delete t.scripts.prepare,delete t.scripts.postinstall,delete t.commitlint,delete t["lint-staged"],t)))},os=async(e,r)=>{r.commitizen||(await T(N.join(e,c.CZRC)),await k(N.join(e,c.PACKAGE_JSON),t=>(delete t.devDependencies[C.COMMITIZEN],delete t.devDependencies[C.CZ_CONVENTIONAL_CHANGELOG],delete t.config?.commitizen,t.config&&Object.keys(t.config).length===0&&delete t.config,delete t.scripts.commit,t)))},is=async(e,r)=>{r.ci||await S(N.join(e,c.GITHUB_WORKFLOWS))},ss=async(e,r)=>{let t=N.join(e,c.GITHUB_DIR);if(!r.keepTemplates)await S(N.join(t,c.GITHUB_ISSUE_TEMPLATE)),await T(N.join(t,c.GITHUB_PR_TEMPLATE));else{let n=N.join(t,c.GITHUB_ISSUE_TEMPLATE),i=N.join(t,c.GITHUB_PR_TEMPLATE),o=s=>s.replace(/Teispace/g,r.company).replace(/support@teispace\.com/g,r.email).replace(/Next\.js Starter/g,r.projectName).replace(/\[AUTHOR\]/g,r.author).replace(/\[COMPANY\]/g,r.company).replace(/\[EMAIL\]/g,r.email);if(m(i)){let s=await u(i);s=o(s),await f(i,s)}try{if(m(n)){let s=await Xr(n);for(let a of s){let p=N.join(n,a),d=await u(p);d=o(d),await f(p,d)}}}catch{}}},as=async(e,r)=>{let t=[c.CODE_OF_CONDUCT,c.CONTRIBUTING,c.SECURITY];for(let n of t)r.communityFiles.includes(n)||await T(N.join(e,n))},cs=async(e,r)=>{if(r.docker){let t=N.join(e,c.ENV_EXAMPLE);if(m(t)){let n=await u(t),i=(o,s)=>{let a=new RegExp(`${o}=.*`);a.test(n)?n=n.replace(a,`${o}=${s}`):n+=`${o}=${s}
|
|
178
|
+
`};i("CONTAINER_NAME",r.containerName||"next-app"),i("IMAGE_NAME",r.imageName||"nextjs-starter"),i("IMAGE_TAG",r.imageTag||"latest"),await f(t,n)}}else{await T(N.join(e,c.DOCKERFILE)),await T(N.join(e,c.DOCKER_COMPOSE)),await T(N.join(e,c.DOCKERIGNORE));let t=N.join(e,c.ENV_EXAMPLE);if(m(t)){let n=await u(t);n=n.replace(/# Docker Compose Configuration\n/,""),n=n.replace(/CONTAINER_NAME=.*\n/,""),n=n.replace(/IMAGE_NAME=.*\n/,""),n=n.replace(/IMAGE_TAG=.*\n/,""),await f(t,n)}}},ps=async(e,r)=>{let t=async o=>{let s=await Xr(o,{withFileTypes:!0}),a=[];for(let p of s){let d=N.join(o,p.name);p.isDirectory()&&p.name!=="node_modules"&&p.name!==".git"?a.push(...await t(d)):p.isFile()&&p.name.toLowerCase()==="readme.md"&&a.push(d)}return a},n=await t(e),i=N.join(e,c.README);for(let o of n)o!==i&&await T(o);if(r.readme){let o=`# ${r.projectName}
|
|
179
179
|
|
|
180
180
|
${r.description}
|
|
181
181
|
|
|
@@ -188,14 +188,14 @@ ${r.packageManager==="npm"?"npm run dev":`${r.packageManager} dev`}
|
|
|
188
188
|
\`\`\`
|
|
189
189
|
|
|
190
190
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
191
|
-
`;await f(
|
|
192
|
-
${
|
|
193
|
-
</NextIntlClientProvider>`),r.darkMode&&(
|
|
194
|
-
${
|
|
195
|
-
</CustomThemeProvider>`),r.redux&&(
|
|
196
|
-
${
|
|
197
|
-
</StoreProvider>`),
|
|
198
|
-
${
|
|
191
|
+
`;await f(i,o)}else await T(i)};w();P();import kt from"node:path";var Kr=async(e,r)=>{let t=[];r.i18n&&t.push("import { type AbstractIntlMessages, NextIntlClientProvider } from 'next-intl';"),r.darkMode&&t.push("import { CustomThemeProvider } from '@/providers';"),r.redux&&t.push("import type { AppState } from '@/store';"),r.i18n&&t.push("import type { SupportedLocale } from '@/types/i18n';"),r.redux&&t.push("import { StoreProvider } from './StoreProvider';");let n=["children: React.ReactNode;"],i=["children"];r.i18n&&(n.push("locale: SupportedLocale;"),n.push("messages: AbstractIntlMessages;"),n.push("timeZone: string;"),i.push("locale","messages","timeZone")),r.redux&&(n.push("preloadedState?: Partial<AppState>;"),i.push("preloadedState"));let o="{children}";r.i18n&&(o=`<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
|
|
192
|
+
${o}
|
|
193
|
+
</NextIntlClientProvider>`),r.darkMode&&(o=`<CustomThemeProvider>
|
|
194
|
+
${o}
|
|
195
|
+
</CustomThemeProvider>`),r.redux&&(o=`<StoreProvider preloadedState={preloadedState}>
|
|
196
|
+
${o}
|
|
197
|
+
</StoreProvider>`),o==="{children}"&&(o="<>{children}</>");let s=`type RootProviderProps = {
|
|
198
|
+
${n.join(`
|
|
199
199
|
`)}
|
|
200
200
|
};`,a=`'use client';
|
|
201
201
|
${t.join(`
|
|
@@ -203,12 +203,12 @@ ${t.join(`
|
|
|
203
203
|
|
|
204
204
|
${s}
|
|
205
205
|
|
|
206
|
-
export const RootProvider = ({ ${
|
|
206
|
+
export const RootProvider = ({ ${i.join(", ")} }: RootProviderProps) => {
|
|
207
207
|
return (
|
|
208
|
-
${
|
|
208
|
+
${o}
|
|
209
209
|
);
|
|
210
210
|
};
|
|
211
|
-
`;await f(
|
|
211
|
+
`;await f(kt.join(e,c.ROOT_PROVIDER),a)},qr=async(e,r)=>{if(r.i18n)return;let t=`\`\${livvic.variable} ${r.darkMode?"bg-light antialiased dark:bg-dark":"antialiased"}\``,n=(r.redux,""),i=`import type { Metadata } from 'next';
|
|
212
212
|
import '@/styles/globals.css';
|
|
213
213
|
import { Livvic } from 'next/font/google';
|
|
214
214
|
import { RootProvider } from '@/providers';
|
|
@@ -233,17 +233,17 @@ export default function RootLayout({
|
|
|
233
233
|
return (
|
|
234
234
|
<html lang="en"${r.darkMode?" suppressHydrationWarning={true}":""}>
|
|
235
235
|
<body className={${t}}>
|
|
236
|
-
<RootProvider${
|
|
236
|
+
<RootProvider${n}>
|
|
237
237
|
{children}
|
|
238
238
|
</RootProvider>
|
|
239
239
|
</body>
|
|
240
240
|
</html>
|
|
241
241
|
);
|
|
242
242
|
}
|
|
243
|
-
`;await f(
|
|
243
|
+
`;await f(kt.join(e,c.ROOT_LAYOUT),i);let o=r.redux?`import { Counter } from '@/features/counter';
|
|
244
244
|
|
|
245
245
|
`:"",s=r.redux?`
|
|
246
|
-
<Counter />`:"",a=`${
|
|
246
|
+
<Counter />`:"",a=`${o}export default function Home() {
|
|
247
247
|
return (
|
|
248
248
|
<div className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
249
249
|
<h1 className="text-4xl font-bold">Welcome to ${r.projectName}</h1>
|
|
@@ -251,14 +251,14 @@ export default function RootLayout({
|
|
|
251
251
|
</div>
|
|
252
252
|
);
|
|
253
253
|
}
|
|
254
|
-
`;await f(
|
|
255
|
-
Cleaning up: Deleting directory ${r.projectName}...`));try{await S(t),console.log(
|
|
256
|
-
Process interrupted. Cleaning up...`)),await
|
|
257
|
-
`)},
|
|
258
|
-
`;var
|
|
259
|
-
`);let a=ge.join(t,"src","components","index.ts");await
|
|
260
|
-
`;r.includes(t.trim())||(r+=t,await f(e,r))};import{writeFile as
|
|
261
|
-
${
|
|
254
|
+
`;await f(kt.join(e,c.ROOT_PAGE),a)};import ls from"degit";var Wr=async e=>{let r=R("Downloading template...");try{await ls("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!0}).clone(e),r.succeed("Template downloaded successfully.")}catch(t){throw r.fail("Failed to download template."),t}};var Zr=e=>{e.command("init").description("Initialize a new Next.js project").argument("[name]","Project name").action(async r=>{await ms(r)})},be=async(e,r,t,n)=>{let i=R(e);n.current=i;try{let o=await t();return i.succeed(r),o}finally{n.current===i&&(n.current=null)}},ms=async e=>{Cr(),l("Welcome to the Teispace Next.js App Creator!"),l("");let r=await br(e),t=Ot.resolve(process.cwd(),r.projectName);m(t)&&(console.error(oe.red(`Error: Directory ${r.projectName} already exists.`)),process.exit(1));let n={current:null},i=async()=>{if(m(t)){n.current?.stop(),n.current=null,console.log(oe.yellow(`
|
|
255
|
+
Cleaning up: Deleting directory ${r.projectName}...`));try{await S(t),console.log(oe.green("Cleanup successful."))}catch(s){console.error(oe.red(`Failed to clean up directory ${r.projectName}:`),s)}}},o=async()=>{console.log(oe.red(`
|
|
256
|
+
Process interrupted. Cleaning up...`)),await i(),process.exit(1)};process.on("SIGINT",o),process.on("SIGTERM",o);try{if(await Wr(t),await Br(t,r),await Jr(t,r),await be("Generating providers and layout...","Providers and layout generated.",async()=>{await Kr(t,r),await qr(t,r)},n),await be("Setting up developer tools...","Developer tools configured.",()=>Yr(t,r),n),await be("Initializing Git...","Git initialized.",()=>Er(t,r.gitRemote,r.pushToRemote),n),await be("Installing dependencies...","Dependencies installed.",()=>Rr(t,r.packageManager),n),await be("Formatting and linting...","Formatted.",()=>U(t,r.packageManager,"lint:fix"),n),r.copyEnv){let s=Ot.join(t,".env.example"),a=Ot.join(t,".env");m(s)&&await(await import("node:fs/promises")).copyFile(s,a)}process.off("SIGINT",o),process.off("SIGTERM",o),l(""),l(oe.green(`\u2728 Project ${r.projectName} created successfully!`)),l(""),l("To get started:"),l(oe.cyan(` cd ${r.projectName}`)),l(oe.cyan(` ${r.packageManager==="npm"?"npm run dev":`${r.packageManager} dev`}`)),l("")}catch(s){n.current?.fail("Failed to create project."),n.current=null,console.error(s),await i(),process.exit(1)}};import Yt from"node:path";import q from"picocolors";L();import{mkdir as nn,writeFile as jt}from"node:fs/promises";import ge from"node:path";P();var Ft=e=>{let{componentName:r,isClient:t,hasI18n:n}=e,i=e.translationNamespace||r,o=[];return(t||n)&&o.push("'use client';"),n&&o.push("import { useTranslations } from 'next-intl';"),o.push(""),o.push(`export function ${r}() {`),n?(o.push(` const t = useTranslations('${i}');`),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>${r}</h2>`),o.push(" </div>"),o.push(" );")),o.push("}"),o.push(""),o.join(`
|
|
257
|
+
`)},rn=e=>`export { ${e.componentName} } from './${e.componentName}';
|
|
258
|
+
`;var Dt=async e=>{let{name:r,projectPath:t,isClient:n,hasI18n:i,featurePath:o}=e,s=$(r);o?await gs(s,t,o,n,i):await hs(s,r,t,n,i)},gs=async(e,r,t,n,i)=>{let o=ge.join(r,t,"components");await nn(o,{recursive:!0}),await jt(ge.join(o,`${e}.tsx`),Ft({componentName:e,isClient:n,hasI18n:i}))},hs=async(e,r,t,n,i)=>{let o=ge.join(t,"src","components","common",e);await nn(o,{recursive:!0}),await jt(ge.join(o,`${e}.tsx`),Ft({componentName:e,isClient:n,hasI18n:i})),await jt(ge.join(o,"index.ts"),rn({componentName:e}));let s=ge.join(t,"src","components","common","index.ts");await xs(s,`export { ${e} } from './${e}';
|
|
259
|
+
`);let a=ge.join(t,"src","components","index.ts");await ys(a)},xs=async(e,r)=>{let t="";m(e)&&(t=await u(e)),t.includes(r.trim())||(t+=r,await f(e,t))},ys=async e=>{let r="";m(e)&&(r=await u(e));let t=`export * from './common';
|
|
260
|
+
`;r.includes(t.trim())||(r+=t,await f(e,r))};import{writeFile as Ps}from"node:fs/promises";import vs from"node:path";var on=e=>{let{camelName:r,pascalName:t,httpClient:n}=e,i=n==="axios"?"import { axiosClient } from '@/lib/utils/http';":"import { fetchClient } from '@/lib/utils/http';",o=n==="axios"?"axiosClient":"fetchClient";return`import { AppApis } from '@/lib/config';
|
|
261
|
+
${i}
|
|
262
262
|
import type { ResultAsync } from '@/types';
|
|
263
263
|
|
|
264
264
|
/**
|
|
@@ -286,33 +286,33 @@ export interface Update${t}Dto {
|
|
|
286
286
|
|
|
287
287
|
export const ${r}Service = {
|
|
288
288
|
getAll: (): ResultAsync<${t}Summary[]> => {
|
|
289
|
-
return ${
|
|
289
|
+
return ${o}.get<${t}Summary[]>(AppApis.${r}.getAll);
|
|
290
290
|
},
|
|
291
291
|
|
|
292
292
|
getById: (id: string): ResultAsync<${t}Detail> => {
|
|
293
|
-
return ${
|
|
293
|
+
return ${o}.get<${t}Detail>(AppApis.${r}.getById(id));
|
|
294
294
|
},
|
|
295
295
|
|
|
296
296
|
create: (data: Create${t}Dto): ResultAsync<${t}Detail> => {
|
|
297
|
-
return ${
|
|
297
|
+
return ${o}.post<${t}Detail>(AppApis.${r}.create, data);
|
|
298
298
|
},
|
|
299
299
|
|
|
300
300
|
update: (id: string, data: Update${t}Dto): ResultAsync<${t}Detail> => {
|
|
301
|
-
return ${
|
|
301
|
+
return ${o}.patch<${t}Detail>(AppApis.${r}.update(id), data);
|
|
302
302
|
},
|
|
303
303
|
|
|
304
304
|
delete: (id: string): ResultAsync<void> => {
|
|
305
|
-
return ${
|
|
305
|
+
return ${o}.delete<void>(AppApis.${r}.delete(id));
|
|
306
306
|
},
|
|
307
307
|
};
|
|
308
|
-
`},
|
|
308
|
+
`},sn=e=>{let{camelName:r,kebabName:t}=e;return` ${r}: {
|
|
309
309
|
base: \`\${API_PREFIX}/${t}\`,
|
|
310
310
|
getAll: \`\${API_PREFIX}/${t}\`,
|
|
311
311
|
getById: (id: string) => \`\${API_PREFIX}/${t}/\${id}\`,
|
|
312
312
|
create: \`\${API_PREFIX}/${t}\`,
|
|
313
313
|
update: (id: string) => \`\${API_PREFIX}/${t}/\${id}\`,
|
|
314
314
|
delete: (id: string) => \`\${API_PREFIX}/${t}/\${id}\`,
|
|
315
|
-
},`};var
|
|
315
|
+
},`};var Lt=async e=>{let{name:r,outputPath:t,httpClient:n}=e,i=A(r),o=$(r);await Ps(vs.join(t,`${r}.service.ts`),on({camelName:i,pascalName:o,httpClient:n}))};import{mkdir as Ts,writeFile as lt}from"node:fs/promises";import V from"node:path";import{writeFile as Ss}from"node:fs/promises";import ws from"node:path";var an=e=>{let{camelName:r,httpClient:t}=e;return`import { AppApis } from '@/lib/config';
|
|
316
316
|
${t==="axios"?"import { axiosClient } from '@/lib/utils/http';":"import { fetchClient } from '@/lib/utils/http';"}
|
|
317
317
|
import type { ResultAsync } from '@/types';
|
|
318
318
|
|
|
@@ -321,30 +321,30 @@ export const ${r}Service = {
|
|
|
321
321
|
return ${t==="axios"?"axiosClient":"fetchClient"}.get<string>(AppApis.${r}.getAll);
|
|
322
322
|
},
|
|
323
323
|
};
|
|
324
|
-
`};var
|
|
324
|
+
`};var Ie=async e=>{let{name:r,outputPath:t,httpClient:n}=e,i=A(r);await Ss(ws.join(t,`${r}.service.ts`),an({camelName:i,httpClient:n}))};import{mkdir as Cs,writeFile as Ae}from"node:fs/promises";import ke from"node:path";var cn=e=>{let{sliceName:r,withPersist:t}=e;return`export * from './${r}.slice';
|
|
325
325
|
export * from './${r}.selectors';
|
|
326
326
|
export * from './${r}.types';${t?`
|
|
327
327
|
export * from './persist';`:""}
|
|
328
|
-
`},
|
|
329
|
-
export { ${
|
|
330
|
-
export * from './types/${r}.types';${
|
|
331
|
-
export * from './store';`:""}${
|
|
328
|
+
`},pn=e=>{let{featureName:r,componentName:t,hookName:n,withStore:i,withService:o}=e;return`export { default as ${t} } from './components/${t}';
|
|
329
|
+
export { ${n} } from './hooks/${n}';
|
|
330
|
+
export * from './types/${r}.types';${i?`
|
|
331
|
+
export * from './store';`:""}${o?`
|
|
332
332
|
export * from './services/${r}.service';`:""}
|
|
333
|
-
`};var
|
|
333
|
+
`};var ln=e=>{let{componentName:r,camelName:t,typesImportPath:n}=e;return`import type { PersistConfig } from 'redux-persist';
|
|
334
334
|
import storage from 'redux-persist/lib/storage';
|
|
335
|
-
import type { ${r}State } from '${
|
|
335
|
+
import type { ${r}State } from '${n}';
|
|
336
336
|
|
|
337
337
|
export const ${t}PersistConfig: PersistConfig<${r}State> = {
|
|
338
338
|
key: '${t}',
|
|
339
339
|
storage,
|
|
340
340
|
// whitelist: ['someField'], // Specify which fields to persist
|
|
341
341
|
};
|
|
342
|
-
`};var
|
|
342
|
+
`};var mn=e=>{let{componentName:r,camelName:t,sliceName:n}=e;return`import { RootState } from '@/store/rootReducer';
|
|
343
343
|
|
|
344
344
|
export const select${r}State = (state: RootState) => state.${t};
|
|
345
|
-
export { setLoading, setError, resetState } from './${
|
|
346
|
-
`};var
|
|
347
|
-
import type { ${r}State } from '${
|
|
345
|
+
export { setLoading, setError, resetState } from './${n}.slice';
|
|
346
|
+
`};var dn=e=>{let{componentName:r,camelName:t,typesImportPath:n}=e;return`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
|
347
|
+
import type { ${r}State } from '${n}';
|
|
348
348
|
|
|
349
349
|
const initialState: ${r}State = {
|
|
350
350
|
loading: false,
|
|
@@ -372,13 +372,13 @@ export const ${t}Slice = createSlice({
|
|
|
372
372
|
export const { setLoading, setError, resetState } = ${t}Slice.actions;
|
|
373
373
|
|
|
374
374
|
export const ${t}Reducer = ${t}Slice.reducer;
|
|
375
|
-
`};var
|
|
375
|
+
`};var pt=e=>{let{componentName:r,withStore:t}=e;return`export interface ${r}State {
|
|
376
376
|
${t?`loading: boolean;
|
|
377
377
|
error: string | null;
|
|
378
378
|
// Add your state properties here`:`// Add your state properties here
|
|
379
379
|
// example: value: string;`}
|
|
380
380
|
}
|
|
381
|
-
`};var
|
|
381
|
+
`};var Oe=async e=>{let{name:r,outputPath:t,persist:n,typesImportPath:i=`./${r}.types`}=e;await Cs(t,{recursive:!0});let o=$(r),s=A(r);await Promise.all([Ae(ke.join(t,`${r}.types.ts`),pt({componentName:o,withStore:!0})),Ae(ke.join(t,`${r}.slice.ts`),dn({componentName:o,camelName:s,typesImportPath:i})),Ae(ke.join(t,`${r}.selectors.ts`),mn({componentName:o,camelName:s,sliceName:r})),...n?[Ae(ke.join(t,"persist.ts"),ln({componentName:o,camelName:s,typesImportPath:i}))]:[],Ae(ke.join(t,"index.ts"),cn({sliceName:r,withPersist:n}))])};var un=e=>{let{componentName:r,hookName:t}=e;return`'use client';
|
|
382
382
|
import { ${t} } from '../hooks/${t}';
|
|
383
383
|
|
|
384
384
|
export function ${r}() {
|
|
@@ -393,9 +393,9 @@ export function ${r}() {
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
export default ${r};
|
|
396
|
-
`};var
|
|
396
|
+
`};var fn=e=>{let{hookName:r,componentName:t,featureName:n}=e;return`'use client';
|
|
397
397
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
|
398
|
-
import { select${t}State, setLoading, setError } from '../store/${
|
|
398
|
+
import { select${t}State, setLoading, setError } from '../store/${n}.selectors';
|
|
399
399
|
|
|
400
400
|
export const ${r} = () => {
|
|
401
401
|
const dispatch = useAppDispatch();
|
|
@@ -417,7 +417,7 @@ export const ${r} = () => {
|
|
|
417
417
|
setError: handleSetError,
|
|
418
418
|
} as const;
|
|
419
419
|
};
|
|
420
|
-
`},
|
|
420
|
+
`},gn=e=>{let{hookName:r}=e;return`'use client';
|
|
421
421
|
import { useState } from 'react';
|
|
422
422
|
|
|
423
423
|
export const ${r} = () => {
|
|
@@ -429,8 +429,8 @@ export const ${r} = () => {
|
|
|
429
429
|
// Add your methods here
|
|
430
430
|
} as const;
|
|
431
431
|
};
|
|
432
|
-
`};var
|
|
433
|
-
`)};var
|
|
432
|
+
`};var Mt=async e=>{let{name:r,outputPath:t,createStore:n,persistStore:i,createService:o,httpClient:s}=e,a=$(r),p=`use${a}`,d=[V.join(t,"components"),V.join(t,"hooks"),V.join(t,"types"),...n?[V.join(t,"store")]:[],...o?[V.join(t,"services")]:[]];await Promise.all(d.map(g=>Ts(g,{recursive:!0}))),await Promise.all([lt(V.join(t,"components",`${a}.tsx`),un({componentName:a,hookName:p})),lt(V.join(t,"hooks",`${p}.ts`),n?fn({hookName:p,componentName:a,featureName:r}):gn({hookName:p})),lt(V.join(t,"types",`${r}.types.ts`),pt({componentName:a,withStore:n})),lt(V.join(t,"index.ts"),pn({featureName:r,componentName:a,hookName:p,withStore:n,withService:o}))]),n&&await Oe({name:r,outputPath:V.join(t,"store"),persist:i,typesImportPath:`../types/${r}.types`}),o&&s&&await Ie({name:r,outputPath:V.join(t,"services"),httpClient:s})};import{mkdir as Es,writeFile as Rs}from"node:fs/promises";import Gt from"node:path";var hn=e=>{let{hookName:r,isClient:t}=e,n=[];return t&&n.push("'use client';"),n.push("import { useState, useCallback } from 'react';"),n.push(""),n.push(`export const ${r} = () => {`),n.push(" const [loading, setLoading] = useState(false);"),n.push(" const [error, setError] = useState<string | null>(null);"),n.push(""),n.push(" const execute = useCallback(async () => {"),n.push(" setLoading(true);"),n.push(" setError(null);"),n.push(" try {"),n.push(" // Add your logic here"),n.push(" } catch (err) {"),n.push(" setError(err instanceof Error ? err.message : 'An error occurred');"),n.push(" } finally {"),n.push(" setLoading(false);"),n.push(" }"),n.push(" }, []);"),n.push(""),n.push(" return {"),n.push(" loading,"),n.push(" error,"),n.push(" execute,"),n.push(" } as const;"),n.push("};"),n.push(""),n.join(`
|
|
433
|
+
`)};var Ht=async e=>{let{name:r,projectPath:t,isClient:n,featurePath:i}=e,s=`use${$(r)}`,a;i?a=Gt.join(t,i,"hooks"):a=Gt.join(t,"src","hooks"),await Es(a,{recursive:!0}),await Rs(Gt.join(a,`${s}.ts`),hn({hookName:s,isClient:n}))};import{mkdir as Ns}from"node:fs/promises";import mt from"node:path";P();var $s=e=>`import { setRequestLocale } from 'next-intl/server';
|
|
434
434
|
import type { SupportedLocale } from '@/types/i18n';
|
|
435
435
|
|
|
436
436
|
type Props = {
|
|
@@ -444,21 +444,21 @@ export default async function ${e}({ children, params }: Props) {
|
|
|
444
444
|
|
|
445
445
|
return <>{children}</>;
|
|
446
446
|
}
|
|
447
|
-
`,
|
|
447
|
+
`,bs=e=>`type Props = {
|
|
448
448
|
children: React.ReactNode;
|
|
449
449
|
};
|
|
450
450
|
|
|
451
451
|
export default function ${e}({ children }: Props) {
|
|
452
452
|
return <>{children}</>;
|
|
453
453
|
}
|
|
454
|
-
`,
|
|
455
|
-
`)}},
|
|
454
|
+
`,xn=({componentName:e,hasI18n:r})=>r?$s(e):bs(e);var Is=/^[a-z][a-z0-9-]*$/,As=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,ks=e=>{if(!Is.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.`)},Os=e=>{if(!As.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},Ut=async e=>{let{segment:r,at:t,projectPath:n,hasI18n:i,withGroup:o}=e;ks(r),t&&Os(t);let s=`${$(r)}Layout`,a=o?`(${r})`:r,p=["src","app"];i&&p.push("[locale]"),t&&p.push(...t.split("/")),p.push(a);let d=mt.join(n,...p),g=mt.join(d,"layout.tsx");if(m(g))throw new Error(`Layout already exists at ${mt.relative(n,g)}.`);return await Ns(d,{recursive:!0}),await f(g,xn({componentName:s,hasI18n:i})),{layoutFile:g,displayPath:mt.relative(n,g),componentName:s}};P();import{copyFile as _s,readFile as Fs,writeFile as js}from"node:fs/promises";import _e from"node:path";var zt=async e=>{let{code:r,name:t,country:n,flag:i,projectPath:o,copyTranslations:s}=e;await Ds(o,r,s),await Ls(o,r),await Ms(o,r,t,n,i)},Ds=async(e,r,t)=>{let n=_e.join(e,"src","i18n","translations"),i=_e.join(n,"en.json"),o=_e.join(n,`${r}.json`);if(t)await _s(i,o);else{let s=await Fs(i,"utf-8"),a=JSON.parse(s),p=yn(a);await js(o,`${JSON.stringify(p,null,2)}
|
|
455
|
+
`)}},yn=e=>{let r={};for(let[t,n]of Object.entries(e))typeof n=="object"&&n!==null?r[t]=yn(n):r[t]="";return r},Ls=async(e,r)=>{let t=_e.join(e,"src","types","i18n.ts");if(!m(t))return;let n=await u(t),i=/export type SupportedLocale = (.*);/,o=n.match(i);if(o&&!o[1].includes(`'${r}'`)){let s=`${o[1]} | '${r}'`;n=n.replace(i,`export type SupportedLocale = ${s};`),await f(t,n)}},Ms=async(e,r,t,n,i)=>{let o=_e.join(e,"src","lib","config","app-locales.ts");if(!m(o))return;let s=await u(o);if(s.includes(`locale: '${r}'`))return;let a=s.lastIndexOf("];");if(a===-1)return;let p=` {
|
|
456
456
|
name: '${t}',
|
|
457
457
|
locale: '${r}',
|
|
458
|
-
flag: '${
|
|
459
|
-
country: '${
|
|
458
|
+
flag: '${i}',
|
|
459
|
+
country: '${n}',
|
|
460
460
|
},
|
|
461
|
-
`;s=s.slice(0,a)+p+s.slice(a),await f(
|
|
461
|
+
`;s=s.slice(0,a)+p+s.slice(a),await f(o,s)};import{mkdir as Gs,writeFile as Vt}from"node:fs/promises";import Fe from"node:path";var Pn=e=>{let{componentName:r,routePath:t,hasI18n:n}=e;return n?`import { generateSEOMetadata } from '@/lib/config/seo';
|
|
462
462
|
import type { Metadata } from 'next';
|
|
463
463
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
464
464
|
import type { SupportedLocale } from '@/types/i18n';
|
|
@@ -504,7 +504,7 @@ export default function ${r}Page() {
|
|
|
504
504
|
</div>
|
|
505
505
|
);
|
|
506
506
|
}
|
|
507
|
-
`},
|
|
507
|
+
`},vn=e=>{let{componentName:r,paramName:t,routePath:n,hasI18n:i}=e;return i?`import { generateSEOMetadata } from '@/lib/config/seo';
|
|
508
508
|
import type { Metadata } from 'next';
|
|
509
509
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
510
510
|
import type { SupportedLocale } from '@/types/i18n';
|
|
@@ -520,7 +520,7 @@ export async function generateMetadata(props: Props): Promise<Metadata> {
|
|
|
520
520
|
return generateSEOMetadata({
|
|
521
521
|
title: t('title'),
|
|
522
522
|
description: t('description'),
|
|
523
|
-
path: '${
|
|
523
|
+
path: '${n}',
|
|
524
524
|
});
|
|
525
525
|
}
|
|
526
526
|
|
|
@@ -558,14 +558,14 @@ export default async function ${r}Page(props: Props) {
|
|
|
558
558
|
</div>
|
|
559
559
|
);
|
|
560
560
|
}
|
|
561
|
-
`},
|
|
561
|
+
`},Sn=e=>`export default function ${e.componentName}Loading() {
|
|
562
562
|
return (
|
|
563
563
|
<div className="flex h-full items-center justify-center">
|
|
564
564
|
<p>Loading...</p>
|
|
565
565
|
</div>
|
|
566
566
|
);
|
|
567
567
|
}
|
|
568
|
-
`,
|
|
568
|
+
`,wn=e=>`'use client';
|
|
569
569
|
|
|
570
570
|
export default function ${e.componentName}Error({
|
|
571
571
|
error,
|
|
@@ -582,32 +582,32 @@ export default function ${e.componentName}Error({
|
|
|
582
582
|
</div>
|
|
583
583
|
);
|
|
584
584
|
}
|
|
585
|
-
`;var
|
|
585
|
+
`;var Jt=async e=>{let{name:r,projectPath:t,hasI18n:n,dynamic:i,withLoading:o=!1,withError:s=!1}=e,a=$(r),p=`/${r}`,d=n?"src/app/[locale]":"src/app",g;i?g=Fe.join(t,d,r,`[${i}]`):g=Fe.join(t,d,r),await Gs(g,{recursive:!0});let h=i?vn({componentName:a,routePath:i?`${p}/[${i}]`:p,paramName:i,hasI18n:n}):Pn({componentName:a,routePath:p,hasI18n:n});await Vt(Fe.join(g,"page.tsx"),h),o&&await Vt(Fe.join(g,"loading.tsx"),Sn({componentName:a})),s&&await Vt(Fe.join(g,"error.tsx"),wn({componentName:a}))};w();import{mkdir as Hs}from"node:fs/promises";import Bt from"node:path";P();var Cn=(e,r)=>e.endsWith(r)?e:`${e}${r}`,Tn=({baseName:e})=>{let r=Cn(e,"Provider"),t=r.replace(/Provider$/,""),n=`${t}ContextValue`,i=`${t}Context`,o=`use${t}`;return`'use client';
|
|
586
586
|
import { createContext, useContext, useMemo } from 'react';
|
|
587
587
|
|
|
588
|
-
type ${
|
|
588
|
+
type ${n} = {
|
|
589
589
|
// TODO: declare context value fields
|
|
590
590
|
};
|
|
591
591
|
|
|
592
|
-
const ${
|
|
592
|
+
const ${i} = createContext<${n} | null>(null);
|
|
593
593
|
|
|
594
|
-
export const ${
|
|
595
|
-
const ctx = useContext(${
|
|
594
|
+
export const ${o} = (): ${n} => {
|
|
595
|
+
const ctx = useContext(${i});
|
|
596
596
|
if (!ctx) {
|
|
597
|
-
throw new Error('${
|
|
597
|
+
throw new Error('${o} must be used within ${r}');
|
|
598
598
|
}
|
|
599
599
|
return ctx;
|
|
600
600
|
};
|
|
601
601
|
|
|
602
602
|
export const ${r} = ({ children }: { children: React.ReactNode }) => {
|
|
603
|
-
const value = useMemo<${
|
|
604
|
-
return <${
|
|
603
|
+
const value = useMemo<${n}>(() => ({}), []);
|
|
604
|
+
return <${i}.Provider value={value}>{children}</${i}.Provider>;
|
|
605
605
|
};
|
|
606
|
-
`},
|
|
606
|
+
`},En=e=>Cn(e,"Provider");var Us=/^[A-Za-z][A-Za-z0-9-]*$/,zs=e=>e.includes("-")?$(e):e.charAt(0).toUpperCase()+e.slice(1),Xt=async e=>{let{name:r,projectPath:t}=e;if(!Us.test(r))throw new Error(`Invalid provider name "${r}". Use kebab-case or PascalCase letters and digits only.`);let n=zs(r).replace(/Provider$/,""),i=En(n),o=i,s=Bt.join(t,c.PROVIDERS),a=Bt.join(s,`${o}.tsx`);if(m(a))throw new Error(`Provider already exists at ${Bt.relative(t,a)}.`);return await Hs(s,{recursive:!0}),await f(a,Tn({baseName:n})),{componentName:i,fileBaseName:o,providerFile:a}};P();import je from"node:path";var Rn=e=>{let{componentName:r,sourceImportPath:t,testUtilsImportPath:n,hasRedux:i,hasI18n:o}=e,s=[];o&&s.push("messages: {}"),i&&s.push("preloadedState: {}");let a=s.length?`, {
|
|
607
607
|
${s.join(`,
|
|
608
608
|
`)},
|
|
609
609
|
}`:"";return`import { describe, expect, it } from 'vitest';
|
|
610
|
-
import { renderWithProviders, screen } from '${
|
|
610
|
+
import { renderWithProviders, screen } from '${n}';
|
|
611
611
|
import { ${r} } from '${t}';
|
|
612
612
|
|
|
613
613
|
describe('${r}', () => {
|
|
@@ -617,9 +617,9 @@ describe('${r}', () => {
|
|
|
617
617
|
expect(screen).toBeDefined();
|
|
618
618
|
});
|
|
619
619
|
});
|
|
620
|
-
`}
|
|
620
|
+
`},$n=e=>{let{hookName:r,sourceImportPath:t,withStore:n,testUtilsImportPath:i}=e;return n?`import { renderHook } from '@testing-library/react';
|
|
621
621
|
import { describe, expect, it } from 'vitest';
|
|
622
|
-
import { TestProviders } from '${
|
|
622
|
+
import { TestProviders } from '${i}';
|
|
623
623
|
import { ${r} } from '${t}';
|
|
624
624
|
|
|
625
625
|
describe('${r}', () => {
|
|
@@ -638,114 +638,120 @@ describe('${r}', () => {
|
|
|
638
638
|
expect(result.current).toBeDefined();
|
|
639
639
|
});
|
|
640
640
|
});
|
|
641
|
-
`},
|
|
642
|
-
import { ${
|
|
641
|
+
`},bn=e=>{let{camelName:r,sourceImportPath:t}=e,n=`${r}Slice`;return`import { describe, expect, it } from 'vitest';
|
|
642
|
+
import { ${n}, resetState, setError, setLoading } from '${t}';
|
|
643
643
|
|
|
644
|
-
describe('${
|
|
644
|
+
describe('${n}', () => {
|
|
645
645
|
const initialState = { loading: false, error: null } as const;
|
|
646
646
|
|
|
647
647
|
it('returns the initial state', () => {
|
|
648
|
-
expect(${
|
|
648
|
+
expect(${n}.reducer(undefined, { type: '@@INIT' })).toEqual(initialState);
|
|
649
649
|
});
|
|
650
650
|
|
|
651
651
|
it('handles setLoading', () => {
|
|
652
|
-
const next = ${
|
|
652
|
+
const next = ${n}.reducer(initialState, setLoading(true));
|
|
653
653
|
expect(next.loading).toBe(true);
|
|
654
654
|
});
|
|
655
655
|
|
|
656
656
|
it('handles setError', () => {
|
|
657
|
-
const next = ${
|
|
657
|
+
const next = ${n}.reducer(initialState, setError('boom'));
|
|
658
658
|
expect(next.error).toBe('boom');
|
|
659
659
|
});
|
|
660
660
|
|
|
661
661
|
it('handles resetState', () => {
|
|
662
|
-
const next = ${
|
|
662
|
+
const next = ${n}.reducer({ loading: true, error: 'boom' }, resetState());
|
|
663
663
|
expect(next).toEqual(initialState);
|
|
664
664
|
});
|
|
665
665
|
});
|
|
666
|
-
`};var Q=async e=>{let{projectPath:r,sourceFile:t,kind:
|
|
666
|
+
`};var Q=async e=>{let{projectPath:r,sourceFile:t,kind:n,symbolName:i}=e,o=je.dirname(t),s=je.basename(t).replace(/\.(tsx?|jsx?)$/i,""),a=n==="component"?".tsx":".ts",p=je.join(o,`${s}.test${a}`),d=`./${s}`,g=Vs(o,je.join(r,"test","test-utils")),h;return n==="component"?h=Rn({componentName:i,sourceImportPath:d,testUtilsImportPath:g,hasRedux:!!e.hasRedux,hasI18n:!!e.hasI18n}):n==="hook"?h=$n({hookName:i,sourceImportPath:d,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?g:void 0}):h=bn({camelName:i,sourceImportPath:d}),await f(p,h),p},Vs=(e,r)=>{let t=je.relative(e,r).replace(/\\/g,"/");return t.startsWith(".")?t:`./${t}`};import Js from"enquirer";var{prompt:Bs}=Js,Nn=async e=>{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"}),r.push({type:"confirm",name:"isClient",message:"Add 'use client' directive?",initial:!1});let t=await Bs(r);return{componentName:e||t.componentName,isClient:t.isClient}};var In=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(r,t)=>{try{let n=process.cwd();l(q.cyan(`
|
|
667
667
|
\u{1F9E9} Component Generator
|
|
668
|
-
`)),
|
|
668
|
+
`)),x.start("Detecting project setup...");let i=await I(n);x.succeed("Project setup detected");let o=await Nn(r),s=$(o.componentName),a=t.client??o.isClient,p=t.i18n??!1,d=Xs(t,i.hasTests);x.start("Generating component..."),await Dt({name:o.componentName,projectPath:n,isClient:a||p,hasI18n:p,featurePath:t.feature}),x.succeed("Component generated");let g=null;if(d){let y=t.feature?Yt.join(n,t.feature,"components",`${s}.tsx`):Yt.join(n,"src/components/common",s,`${s}.tsx`);x.start("Generating test..."),g=await Q({projectPath:n,sourceFile:y,kind:"component",symbolName:s,hasRedux:i.hasRedux,hasI18n:i.hasI18n}),x.succeed("Test generated")}let h=t.feature?`${t.feature}/components/${s}.tsx`:`src/components/common/${s}/${s}.tsx`;l(q.green(`
|
|
669
669
|
\u2728 Component '${s}' created successfully!
|
|
670
|
-
`)),l(q.dim(` \u{1F4C4} ${
|
|
671
|
-
${
|
|
670
|
+
`)),l(q.dim(` \u{1F4C4} ${h}`)),t.feature||(l(q.dim(` \u{1F4C4} src/components/common/${s}/index.ts`)),l(q.dim(" \u{1F4DD} Updated src/components/common/index.ts")),l(q.dim(" \u{1F4DD} Updated src/components/index.ts"))),g&&l(q.dim(` \u{1F9EA} ${Yt.relative(n,g)}`)),l(""),l(q.cyan("Usage:")),t.feature?l(q.dim(` import { ${s} } from '${t.feature.replace(/^src\//,"@/")}/components/${s}';`)):l(q.dim(` import { ${s} } from '@/components';`)),l("")}catch(n){x.fail("Component generation failed"),v(`${n}`),process.exit(1)}})},Xs=(e,r)=>e.noTest?!1:e.test?!0:r;import j from"picocolors";w();P();import Ks from"node:path";w();import An from"node:path";import Ys from"picocolors";P();var De=async e=>{let r=R("Adding @next/bundle-analyzer...");try{let t=An.join(e,c.NEXT_CONFIG);if(!m(t))throw new Error(`${c.NEXT_CONFIG} not found.`);let n=await u(t);if(n.includes("@next/bundle-analyzer")){r.fail("@next/bundle-analyzer is already configured.");return}n=`import withBundleAnalyzer from '@next/bundle-analyzer';
|
|
671
|
+
${n}`;let i=/export default (.*?);/,o=n.match(i);if(!o)throw new Error("Could not locate `export default` in next.config.ts.");let a=`const bundleAnalyzer = withBundleAnalyzer({
|
|
672
672
|
enabled: process.env.ANALYZE === 'true',
|
|
673
673
|
});
|
|
674
674
|
|
|
675
|
-
export default bundleAnalyzer(${
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
675
|
+
export default bundleAnalyzer(${o[1]});`;n=n.replace(i,a),await f(t,n),await k(An.join(e,c.PACKAGE_JSON),p=>(p.scripts=p.scripts??{},p.scripts.analyze||(p.scripts.analyze="ANALYZE=true next build"),p)),r.text="Installing @next/bundle-analyzer...",await ot(e,"@next/bundle-analyzer"),r.succeed(Ys.green("@next/bundle-analyzer configured."))}catch(t){throw r.fail("Failed to add @next/bundle-analyzer."),t}};var Kt=(e,r)=>{let t=e.split(`
|
|
676
|
+
`),n=[],i=new RegExp(`^import\\s+${r}\\s+from\\s+['"][^'"]+['"];?\\s*$`),o=/^(import\s+)\{([^}]+)\}(\s+from\s+['"][^'"]+['"];?\s*)$/;for(let s of t){if(i.test(s))continue;let a=s.match(o);if(a){let p=a[2].split(",").map(g=>g.trim()).filter(Boolean),d=p.filter(g=>g.replace(/^type\s+/,"").split(/\s+as\s+/)[0].trim()!==r);if(d.length===0)continue;if(d.length<p.length){n.push(`${a[1]}{ ${d.join(", ")} }${a[3]}`);continue}}n.push(s)}return n.join(`
|
|
677
|
+
`)},Se=e=>r=>{let t=r.split(`
|
|
678
|
+
`),n=new RegExp(`^([ \\t]*)<${e}\\b`),i=-1,o="";for(let h=0;h<t.length;h++){let y=t[h].match(n);if(y){i=h,o=y[1];break}}if(i===-1||!/>\s*$/.test(t[i]))return null;let s=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^${s}<\\/${e}>\\s*$`),p=-1;for(let h=i+1;h<t.length;h++)if(a.test(t[h])){p=h;break}if(p===-1)return null;let d=t.slice(i+1,p).map(h=>h.startsWith(" ")?h.slice(2):h),g=[...t.slice(0,i),...d,...t.slice(p+1)].join(`
|
|
679
|
+
`);return Kt(g,e)};var kn=e=>{let r=e.split(`
|
|
680
|
+
`).filter(n=>!/^import\s+createNextIntlPlugin\s+from\s+['"]next-intl\/plugin['"];?\s*$/.test(n)).join(`
|
|
681
|
+
`);r=r.replace(/^const\s+withNextIntl\s*=\s*createNextIntlPlugin\([^)]*\);\s*\n?/m,"");let t=/withNextIntl\(\s*((?:[^()]|\([^()]*\))+?)\s*\)/;return t.test(r)?(r=r.replace(t,"$1"),r):null},On=e=>{let r=Kt(e,"withBundleAnalyzer"),t=/^const\s+bundleAnalyzer\s*=\s*withBundleAnalyzer\(\{[\s\S]*?\}\);\s*\n?/m;if(!t.test(r))return null;r=r.replace(t,"");let n=/bundleAnalyzer\(\s*((?:[^()]|\([^()]*\))+?)\s*\)/;return n.test(r)?(r=r.replace(n,"$1"),r):null};var _n={id:"bundle-analyzer",name:"Bundle Analyzer",description:"@next/bundle-analyzer wrapped around the default export",detect:async e=>{let r=Ks.join(e,c.NEXT_CONFIG);return m(r)?/@next\/bundle-analyzer/.test(await u(r)):!1},files:[],packages:[{name:"@next/bundle-analyzer",kind:"devDependency"}],scripts:[{name:"analyze",expectedValue:"ANALYZE=true next build"}],injections:[{file:c.NEXT_CONFIG,description:"withBundleAnalyzer import + wrapping",presence:/@next\/bundle-analyzer/,removePattern:On}],apply:De};w();P();import Zs from"node:path";w();import Fn from"node:path";import qs from"picocolors";P();var qt=e=>{let r={...e.scripts??{}},t={...e.devDependencies??{}};return r.commit||(r.commit="cz"),t[C.COMMITIZEN]||(t[C.COMMITIZEN]="*"),t[C.CZ_CONVENTIONAL_CHANGELOG]||(t[C.CZ_CONVENTIONAL_CHANGELOG]="*"),{...e,scripts:r,devDependencies:t}},Wt=e=>{let r=e.devDependencies??{},t=[];return r[C.COMMITIZEN]||t.push(C.COMMITIZEN),r[C.CZ_CONVENTIONAL_CHANGELOG]||t.push(C.CZ_CONVENTIONAL_CHANGELOG),t};var Ws=`${JSON.stringify({path:"cz-conventional-changelog"},null,2)}
|
|
682
|
+
`,Le=async e=>{let r=R("Setting up Commitizen...");try{let t=Fn.join(e,c.CZRC);m(t)||await f(t,Ws);let n=Fn.join(e,c.PACKAGE_JSON),i=JSON.parse(await u(n)),o=Wt(i);if(await k(n,s=>qt(s)),o.length>0){r.text=`Installing ${o.join(", ")}...`;let s=await O(e);await ue(e,s,o)}r.succeed(qs.green("Commitizen configured. Use `commit` to start a guided commit."))}catch(t){throw r.fail("Failed to set up Commitizen."),t}};var jn={id:"commitizen",name:"Commitizen",description:"Guided conventional-commit prompt via `commit` script",detect:async e=>m(Zs.join(e,c.CZRC)),files:[{path:c.CZRC,generated:!0}],packages:[{name:"commitizen",kind:"devDependency"},{name:"cz-conventional-changelog",kind:"devDependency"}],scripts:[{name:"commit",expectedValue:"cz"}],injections:[],apply:Le};w();P();import ra from"node:path";import ea from"node:path";import ta from"picocolors";P();w();P();import Dn from"node:path";import Qs from"degit";var Ln=async(e,r)=>{r.text="Fetching assets from starter repo...",await Qs("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},Mn=async(e,r)=>{let t=Dn.join(e,c.THEME_PROVIDER),n=Dn.join(r,"src/providers/CustomThemeProvider.tsx");await _(n,t)};w();P();import Me from"node:path";w();P();import Gn from"node:path";var dt=async e=>{let r=[Gn.join(e,c.ROOT_LAYOUT),Gn.join(e,"src/app/[locale]/layout.tsx")];for(let t of r)if(m(t)&&(await u(t)).includes("<body"))return t;for(let t of r)if(m(t))return t;return""};var Hn=async e=>{let r=Me.join(e,c.THEME_PROVIDER),t=Me.join(e,c.GLOBALS_CSS),n=Me.join(e,"package.json");if(m(r))return{isSetup:!0,reason:"CustomThemeProvider.tsx exists"};if(m(n)){let i=JSON.parse(await u(n)),o="@teispace/next-themes";if(i.dependencies?.[o]||i.devDependencies?.[o])return{isSetup:!0,reason:`${o} is installed`}}return m(t)&&(await u(t)).includes("@custom-variant dark")?{isSetup:!0,reason:"Dark theme CSS found in globals.css"}:{isSetup:!1,reason:""}},Un=async e=>{let r=Me.join(e,c.GLOBALS_CSS),t=Me.join(e,c.PROVIDERS_INDEX),n=await dt(e);if(!m(r)||!n||!m(t))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 n};w();P();import ut from"node:path";var zn=async e=>{let r=ut.join(e,c.PROVIDERS_INDEX),t=await u(r);t.includes("CustomThemeProvider")||(t+=`export * from './CustomThemeProvider';
|
|
683
|
+
`,await f(r,t))},Vn=async e=>{let r=ut.join(e,"src/providers/RootProvider.tsx");if(!m(r))return;let t=await u(r),n="'use client';",i=t.includes(n);if(i&&(t=t.replace(n,"").trim()),t.includes("CustomThemeProvider")||(/import\s*\{[^}]*\}\s*from\s*'@\/providers'/.test(t)?t=t.replace(/(import\s*\{)([^}]*)(\}\s*from\s*'@\/providers')/,(o,s,a,p)=>`${s}${a.trim()?`${a.trim()}, CustomThemeProvider `:" CustomThemeProvider "}${p}`):t=`import { CustomThemeProvider } from '@/providers';
|
|
684
|
+
${t}`),i&&(t=`${n}
|
|
685
|
+
${t}`),!t.includes("<CustomThemeProvider"))if(t.includes("{children}"))t=t.replace(/\{children\}/,"<CustomThemeProvider>{children}</CustomThemeProvider>");else{let o=t.match(/return\s*\(\s*([\s\S]*?)\s*\);/);o&&(t=t.replace(o[0],`return (
|
|
680
686
|
<CustomThemeProvider>
|
|
681
|
-
${
|
|
687
|
+
${o[1]}
|
|
682
688
|
</CustomThemeProvider>
|
|
683
|
-
);`))}await f(r,t)},
|
|
689
|
+
);`))}await f(r,t)},Jn=async(e,r)=>{let t=ut.join(e,c.GLOBALS_CSS),n=ut.join(r,"src/styles/globals.css"),i="";if(m(n)){let s=await u(n),a=s.match(/@custom-variant dark \(.*?\);/),p=s.match(/@theme \{[\s\S]*?\}/);a&&(i+=`
|
|
684
690
|
${a[0]}
|
|
685
|
-
`),p&&(
|
|
691
|
+
`),p&&(i+=`
|
|
686
692
|
${p[0]}
|
|
687
|
-
`)}
|
|
693
|
+
`)}i||(i=`
|
|
688
694
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
689
695
|
|
|
690
696
|
@theme {
|
|
691
697
|
--color-dark: #202938;
|
|
692
698
|
--color-light: #f5f5f5;
|
|
693
699
|
}
|
|
694
|
-
`);let
|
|
695
|
-
`,s);
|
|
700
|
+
`);let o=await u(t);if(!o.includes("@custom-variant dark")){if(o.includes("@import")){let s=o.lastIndexOf("@import"),a=o.indexOf(`
|
|
701
|
+
`,s);o=o.slice(0,a+1)+i+o.slice(a+1)}else o=i+o;await f(t,o)}},Bn=async e=>{let r=await u(e);r.includes("dark:bg-dark")||(r=r.replace(/className="([^"]*)"/,'className="$1 bg-light dark:bg-dark"'),await f(e,r))};var Ge=async e=>{let r=R("Setting up Dark Theme..."),t=ea.join(e,".next-maker-temp");try{let{isSetup:n,reason:i}=await Hn(e);if(n){r.fail(`Dark Theme is already set up (${i}).`);return}let o=await Un(e);await Ln(t,r),await Mn(e,t),await zn(e),await Vn(e),await Jn(e,t),await Bn(o),r.text="Installing @teispace/next-themes...",await z(e,"@teispace/next-themes"),r.text="Formatting code...";let s=await O(e);await U(e,s,"lint:fix"),r.succeed(ta.green("Dark Theme setup successfully!"))}catch(n){throw r.fail("Failed to setup Dark Theme."),n}finally{await S(t)}};var Xn={id:"dark-theme",name:"Dark Theme",description:"@teispace/next-themes + CustomThemeProvider",detect:async e=>m(ra.join(e,c.THEME_PROVIDER)),files:[{path:c.THEME_PROVIDER,generated:!0}],packages:[{name:"@teispace/next-themes",kind:"dependency"}],scripts:[],injections:[{file:"src/providers/RootProvider.tsx",description:"<CustomThemeProvider> wrap",presence:/CustomThemeProvider/,removePattern:Se("CustomThemeProvider")}],apply:Ge};import oa from"node:fs/promises";import ft from"node:path";import gt from"enquirer";import Zt from"picocolors";P();w();P();import ee from"node:fs/promises";import E from"node:path";import na from"degit";var Yn=async(e,r)=>{r.text="Downloading assets from starter repository...",await na("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},Kn=async(e,r,t,n)=>{let i=E.join(e,c.HTTP_UTILS),o=E.join(r,c.HTTP_UTILS);await ee.cp(E.join(r,c.ERRORS_DIR),E.join(e,c.ERRORS_DIR),{recursive:!0});let s=E.join(e,c.APP_APIS);m(s)||await _(E.join(r,c.APP_APIS),s);let a=E.join(e,c.CONSTANTS);if(!m(a))await _(E.join(r,c.CONSTANTS),a);else{let d=await u(a),g=!1;d.includes("API_RESPONSE_DATA_KEY")||(d+=`
|
|
696
702
|
export const API_RESPONSE_DATA_KEY = 'data';
|
|
697
|
-
`,g=!0),
|
|
703
|
+
`,g=!0),d.includes("SAVE_AUTH_TOKENS")||(d+=`
|
|
698
704
|
export const SAVE_AUTH_TOKENS = false;
|
|
699
|
-
`,g=!0),g&&await f(a,
|
|
705
|
+
`,g=!0),g&&await f(a,d)}let p=E.join(e,c.STORAGE_SERVICE);if(m(p)||await ee.cp(E.join(r,c.STORAGE_SERVICE),p,{recursive:!0}),await ee.cp(E.join(r,c.COMMON_TYPES_DIR),E.join(e,c.COMMON_TYPES_DIR),{recursive:!0}),await ee.cp(E.join(r,c.UTILITY_TYPES_DIR),E.join(e,c.UTILITY_TYPES_DIR),{recursive:!0}),await _(E.join(o,"token-store.ts"),E.join(i,"token-store.ts")),await _(E.join(o,"client-utils.ts"),E.join(i,"client-utils.ts")),n&&n.length>0)for(let d of n){let g=E.join(e,d==="fetch"?c.FETCH_CLIENT:c.AXIOS_CLIENT);await S(g)}for(let d of t)d==="fetch"?await ee.cp(E.join(r,c.FETCH_CLIENT),E.join(e,c.FETCH_CLIENT),{recursive:!0}):await ee.cp(E.join(r,c.AXIOS_CLIENT),E.join(e,c.AXIOS_CLIENT),{recursive:!0});await _(E.join(o,"index.ts"),E.join(i,"index.ts")),await _(E.join(r,c.HTTP_TYPES),E.join(e,c.HTTP_TYPES))},qn=async e=>{let{isFileUsed:r,isStringUsed:t}=await Promise.resolve().then(()=>(it(),Or));await S(E.join(e,c.HTTP_UTILS));let n=E.join(e,c.STORAGE_SERVICE);m(n)&&(await t(e,"SecureStorageService",[n])||await S(n));let i=E.join(e,c.CONSTANTS);if(m(i)){let p=await u(i),d=!1;await t(e,"API_RESPONSE_DATA_KEY",[i])||(p=p.replace(/export const API_RESPONSE_DATA_KEY = .*;\n?/g,""),d=!0),await t(e,"SAVE_AUTH_TOKENS",[i])||(p=p.replace(/export const SAVE_AUTH_TOKENS = .*;\n?/g,""),d=!0),d&&(p=`${p.replace(/\n\s*\n/g,`
|
|
700
706
|
|
|
701
707
|
`).trim()}
|
|
702
|
-
`,await f(
|
|
703
|
-
${t.map(
|
|
704
|
-
`)}`)};
|
|
705
|
-
${t}`;let
|
|
708
|
+
`,await f(i,p))}let o=E.join(e,c.ERRORS_DIR);if(m(o)){let p=await ee.readdir(o),d=!1;for(let g of p){let h=E.parse(g).name;if(await r(e,h,[E.join(o,g)])){d=!0;break}}d||await S(o)}let s=E.join(e,c.COMMON_TYPES_DIR);m(s)&&(await ee.readdir(s)).length===0&&await S(s);let a=E.join(e,c.UTILITY_TYPES_DIR);m(a)&&(await ee.readdir(a)).length===0&&await S(a)};w();P();import He from"node:path";var Wn=async e=>{let r=He.join(e,c.FETCH_CLIENT_FILE),t=He.join(e,c.AXIOS_CLIENT_FILE),n=m(r),i=m(t);return n&&i?{status:"both",reason:"Both Fetch and Axios clients are detected"}:n?{status:"fetch",reason:"Fetch client is already setup"}:i?{status:"axios",reason:"Axios client is already setup"}:{status:"none"}},Zn=async e=>{let t=[He.join(e,"src/lib"),He.join(e,"src/types"),He.join(e,"src/lib/config")].filter(n=>!m(n));if(t.length>0)throw new Error(`Invalid project structure. Missing directories:
|
|
709
|
+
${t.map(n=>`- ${n}`).join(`
|
|
710
|
+
`)}`)};it();var Ue=async e=>{let r=R("Checking HTTP Client setup..."),t=ft.join(e,".next-maker-temp-http");try{let{status:n,reason:i}=await Wn(e);r.stop();let o=await ia(n,i);if(o.type==="cancel"){console.log(Zt.yellow("Setup cancelled."));return}if(r.start("Setting up HTTP client(s)..."),await Zn(e),await Yn(t,r),o.type==="replace"&&o.fromClient&&(r.text="Migrating client usages...",await It(e,o.fromClient,o.clients[0])),o.type!=="remove-both"&&o.type!=="remove-one"){r.text="Copying client files...";let Y=o.type==="replace"&&o.fromClient?[o.fromClient]:void 0;await Kn(e,t,o.clients,Y)}if(o.type==="remove-one"||o.type==="remove-both"){r.text="Removing client files...";let{PROJECT_PATHS:Y}=await Promise.resolve().then(()=>(w(),Nr));for(let tt of o.clients){let rt=ft.join(e,tt==="fetch"?Y.FETCH_CLIENT:Y.AXIOS_CLIENT);await S(rt)}Qn(n,o).length===0&&(r.text="Performing full cleanup...",await qn(e),await At(e))}let s=Qn(n,o);s.length>0&&(r.text="Configuring client...",await Rt(e,s),await $t(e),await bt(e),await Nt(e)),await ve(e,s),r.text="Managing dependencies...";let a=ft.join(e,"package.json"),p=JSON.parse(await oa.readFile(a,"utf-8")),d={...p.dependencies,...p.devDependencies};s.includes("axios")?d.axios||(r.text="Installing Axios...",await z(e,"axios")):d.axios&&(r.text="Uninstalling Axios...",await Tt(e,"axios"));let g=ft.join(e,"src/services/storage");s.length>0||m(g)?d["react-secure-storage"]||(r.text="Installing react-secure-storage...",await z(e,"react-secure-storage")):d["react-secure-storage"]&&(r.text="Uninstalling react-secure-storage...",await Tt(e,"react-secure-storage")),r.text="Formatting code...";let y=await O(e);await U(e,y,"lint:fix"),r.succeed(Zt.green(sa(o)))}catch(n){throw r.fail("Failed to setup HTTP Client."),n}finally{await S(t)}};async function ia(e,r){if(e==="none"){let{choice:n}=await gt.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 n==="both"?{type:"install",clients:["fetch","axios"]}:{type:"install",clients:[n]}}if(e==="fetch"){let{action:n}=await gt.prompt({type:"select",name:"action",message:`${r}. 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(n==="cancel")return{type:"cancel",clients:[]};if(n==="add")return{type:"add",clients:["axios"]};if(n==="replace")return{type:"replace",clients:["axios"],fromClient:"fetch"};if(n==="remove")return{type:"remove-one",clients:["fetch"]}}if(e==="axios"){let{action:n}=await gt.prompt({type:"select",name:"action",message:`${r}. 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(n==="cancel")return{type:"cancel",clients:[]};if(n==="add")return{type:"add",clients:["fetch"]};if(n==="replace")return{type:"replace",clients:["fetch"],fromClient:"axios"};if(n==="remove")return{type:"remove-one",clients:["axios"]}}console.log(Zt.yellow(`\u2716 ${r}`));let{action:t}=await gt.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 t==="cancel"?{type:"cancel",clients:[]}:t==="remove-fetch"?{type:"remove-one",clients:["fetch"]}:t==="remove-axios"?{type:"remove-one",clients:["axios"]}:t==="remove-both"?{type:"remove-both",clients:["fetch","axios"]}:{type:"cancel",clients:[]}}function Qn(e,r){return r.type==="install"?r.clients:r.type==="add"?[...e==="fetch"?["fetch"]:["axios"],...r.clients]:r.type==="replace"?r.clients:r.type==="remove-one"?e==="both"?r.clients[0]==="fetch"?["axios"]:["fetch"]:[]:[]}function sa(e){if(e.type==="install")return`HTTP Client (${e.clients.map(t=>t==="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 r=e.fromClient==="fetch"?"Fetch":"Axios",t=e.clients[0]==="fetch"?"Fetch":"Axios";return`Replaced ${r} with ${t} 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!"}var eo={id:"http-client",name:"HTTP Client",description:"Axios and/or Fetch client utilities",detect:async e=>{let{detectProjectSetup:r}=await Promise.resolve().then(()=>(L(),Ne));return(await r(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"}],packages:[],scripts:[],injections:[],apply:Ue};w();import Qt from"node:path";import la from"picocolors";P();w();P();import aa from"node:fs/promises";import se from"node:path";import ca from"degit";var to=async(e,r)=>{r.text="Fetching assets from starter repo...",await ca("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},ro=async(e,r)=>{let t=se.join(r,"src/i18n"),n=se.join(e,c.I18N_DIR);await aa.cp(t,n,{recursive:!0});let i=se.join(r,"src/proxy.ts"),o=se.join(e,c.PROXY);await _(i,o);let s=se.join(r,"src/types/i18n.ts"),a=se.join(e,c.I18N_TYPES);await _(s,a);let p=se.join(r,"src/lib/config/app-locales.ts"),d=se.join(e,c.APP_LOCALES);await _(p,d)};w();P();import ht from"node:path";var no=async e=>{let r=ht.join(e,c.I18N_DIR),t=ht.join(e,"package.json");if(m(r))return{isSetup:!0,reason:"src/i18n directory exists"};if(m(t)){let n=JSON.parse(await u(t));if(n.dependencies?.["next-intl"]||n.devDependencies?.["next-intl"])return{isSetup:!0,reason:"next-intl is installed"}}return{isSetup:!1,reason:""}},oo=async e=>{let r=ht.join(e,c.ROOT_LAYOUT),t=ht.join(e,"src/app/[locale]/layout.tsx");if(!m(r)&&!m(t))throw new Error("Project structure mismatch. Ensure src/app/layout.tsx exists.")};w();P();import pa from"node:fs/promises";import te from"node:path";var io=async e=>{let r=te.join(e,c.NEXT_CONFIG);if(m(r)){let t=await u(r);if(!t.includes("createNextIntlPlugin")){t=`import createNextIntlPlugin from 'next-intl/plugin';
|
|
711
|
+
${t}`;let n=/export default (.*?);/;n.test(t)?t=t.replace(n,`
|
|
706
712
|
const withNextIntl = createNextIntlPlugin();
|
|
707
713
|
export default withNextIntl($1);`):(t+=`
|
|
708
714
|
const withNextIntl = createNextIntlPlugin();
|
|
709
715
|
`,t+=`export default withNextIntl(nextConfig);
|
|
710
|
-
`),await f(r,t)}}},
|
|
711
|
-
`,await f(r,t))}},
|
|
712
|
-
`,await f(r,t))}},
|
|
716
|
+
`),await f(r,t)}}},so=async e=>{let r=te.join(e,c.TYPES_INDEX);if(m(r)){let t=await u(r);t.includes("./i18n")||(t+=`export * from './i18n';
|
|
717
|
+
`,await f(r,t))}},ao=async e=>{let r=te.join(e,c.CONFIG_INDEX);if(m(r)){let t=await u(r);t.includes("./app-locales")||(t+=`export * from './app-locales';
|
|
718
|
+
`,await f(r,t))}},co=async e=>{let r=te.join(e,"src/providers/RootProvider.tsx");if(m(r)){let t=await u(r),n="'use client';",i=t.includes(n);if(i&&(t=t.replace(n,"").trim()),t.includes("next-intl")||(t=`import { type AbstractIntlMessages, NextIntlClientProvider } from 'next-intl';
|
|
713
719
|
import type { SupportedLocale } from '@/types/i18n';
|
|
714
|
-
`+t),
|
|
715
|
-
${t}`),!t.includes("locale: SupportedLocale")){let
|
|
720
|
+
`+t),i&&(t=`${n}
|
|
721
|
+
${t}`),!t.includes("locale: SupportedLocale")){let o=` locale: SupportedLocale;
|
|
716
722
|
messages: AbstractIntlMessages;
|
|
717
|
-
timeZone: string;`,s=/(type\s+RootProviderProps\s*=\s*\{)([\s\S]*?)(\};?)/,a=/export const RootProvider = \(\{([\s\S]*?)\}: \{([\s\S]*?)\}\) => \{/;if(s.test(t))t=t.replace(s,(p,
|
|
718
|
-
${
|
|
719
|
-
${
|
|
720
|
-
${
|
|
723
|
+
timeZone: string;`,s=/(type\s+RootProviderProps\s*=\s*\{)([\s\S]*?)(\};?)/,a=/export const RootProvider = \(\{([\s\S]*?)\}: \{([\s\S]*?)\}\) => \{/;if(s.test(t))t=t.replace(s,(p,d,g,h)=>{let y=g.replace(/\s+$/,"");return`${d}${y}
|
|
724
|
+
${o}
|
|
725
|
+
${h}`}),t=t.replace(/export const RootProvider = \(\{([\s\S]*?)\}:\s*RootProviderProps\)/,(p,d)=>{let g=d.trim().replace(/,$/,"");return`export const RootProvider = ({ ${g?`${g}, `:""}locale, messages, timeZone }: RootProviderProps)`});else{let p=t.match(a);if(p){let d=p[1].trim().replace(/,$/,""),g=p[2].trim().replace(/;$/,""),h=`
|
|
726
|
+
${d?`${d},
|
|
721
727
|
`:""}locale,
|
|
722
728
|
messages,
|
|
723
729
|
timeZone,`,y=`
|
|
724
730
|
${g?`${g};
|
|
725
731
|
`:""}locale: SupportedLocale;
|
|
726
732
|
messages: AbstractIntlMessages;
|
|
727
|
-
timeZone: string;`;t=t.replace(p[0],`export const RootProvider = ({${
|
|
733
|
+
timeZone: string;`;t=t.replace(p[0],`export const RootProvider = ({${h}
|
|
728
734
|
}: {${y}
|
|
729
|
-
}) => {`)}}}if(!t.includes("<NextIntlClientProvider")){let
|
|
735
|
+
}) => {`)}}}if(!t.includes("<NextIntlClientProvider")){let o=t.match(/return\s*(?:\(\s*)?([\s\S]*?)(?:\s*\))?;/);if(o){let s=o[1],a=`<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
|
|
730
736
|
{children}
|
|
731
|
-
</NextIntlClientProvider>`;if(s.includes("{children}")){let p=s.replace("{children}",a);t=t.replace(
|
|
737
|
+
</NextIntlClientProvider>`;if(s.includes("{children}")){let p=s.replace("{children}",a);t=t.replace(o[0],`return (
|
|
732
738
|
${p}
|
|
733
|
-
);`)}else t=t.replace(
|
|
739
|
+
);`)}else t=t.replace(o[0],`return (
|
|
734
740
|
<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
|
|
735
741
|
${s}
|
|
736
742
|
</NextIntlClientProvider>
|
|
737
|
-
);`)}}await f(r,t)}},
|
|
743
|
+
);`)}}await f(r,t)}},po=async e=>{let r=te.join(e,c.ROOT_LAYOUT),t=te.join(e,c.ROOT_PAGE),n=te.join(e,"src/app/[locale]");if(await pa.mkdir(n,{recursive:!0}),m(r)){let i=te.join(n,"layout.tsx"),o=await u(r);if(o.includes("next-intl")||(o=`import { routing } from '@/i18n/routing';
|
|
738
744
|
import { hasLocale } from 'next-intl';
|
|
739
745
|
import { notFound } from 'next/navigation';
|
|
740
746
|
import { getMessages, getTimeZone, setRequestLocale } from 'next-intl/server';
|
|
741
|
-
${
|
|
747
|
+
${o}`),!o.includes("generateStaticParams")){let p=o.indexOf("};",o.indexOf("export const metadata"));if(p!==-1){let d=p+2;o=o.slice(0,d)+`
|
|
742
748
|
|
|
743
749
|
export function generateStaticParams() {
|
|
744
750
|
return routing.locales.map((locale) => ({ locale }));
|
|
745
|
-
}`+
|
|
751
|
+
}`+o.slice(d)}}o=o.replace(/Readonly<\{\s*children:\s*React\.ReactNode;\s*}>/,`Readonly<{
|
|
746
752
|
children: React.ReactNode;
|
|
747
753
|
params: Promise<{ locale: string }>;
|
|
748
|
-
}>`);let s=/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,a=
|
|
754
|
+
}>`);let s=/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,a=o.match(s);if(a){a[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 d=/export default async function RootLayout[\s\S]*?\)\s*\{/,g=o.match(d);if(g&&g.index!==void 0){let h=g.index+g[0].length-1;o=o.slice(0,h+1)+`
|
|
749
755
|
const { locale } = await params;
|
|
750
756
|
|
|
751
757
|
if (!hasLocale(routing.locales, locale)) {
|
|
@@ -755,42 +761,42 @@ export function generateStaticParams() {
|
|
|
755
761
|
setRequestLocale(locale);
|
|
756
762
|
|
|
757
763
|
const [messages, timeZone] = await Promise.all([getMessages(), getTimeZone()]);
|
|
758
|
-
`+
|
|
764
|
+
`+o.slice(h+1)}o=o.replace(/<RootProvider>/,"<RootProvider locale={locale} messages={messages} timeZone={timeZone}>"),o=o.replace(/<html lang="en"/,"<html lang={locale}")}await f(i,o),await T(r)}if(m(t)){let i=te.join(n,"page.tsx"),o=await u(t);o.includes("next-intl")||(o=`import { SupportedLocale } from '@/types/i18n';
|
|
759
765
|
import { setRequestLocale } from 'next-intl/server';
|
|
760
|
-
${
|
|
766
|
+
${o}`),o=o.replace(/export default function Home\(\)/,`type Props = {
|
|
761
767
|
params: Promise<{ locale: string }>;
|
|
762
768
|
};
|
|
763
769
|
|
|
764
|
-
export default async function Home(props: Props)`);let s=
|
|
770
|
+
export default async function Home(props: Props)`);let s=o.indexOf("export default async function Home(props: Props) {");if(s!==-1){let a=`
|
|
765
771
|
const locale = (await props.params).locale as SupportedLocale;
|
|
766
772
|
setRequestLocale(locale);
|
|
767
|
-
`,p=
|
|
768
|
-
reactCompiler: true,`),await f(t,
|
|
773
|
+
`,p=o.indexOf("{",s);o=o.slice(0,p+1)+a+o.slice(p+1)}await f(i,o),await T(t)}};var ze=async e=>{let r=R("Setting up Internationalization (next-intl)..."),t=Qt.join(e,".next-maker-temp-i18n");try{let{isSetup:n,reason:i}=await no(e);if(n){r.fail(`i18n is already set up (${i}).`);return}await oo(e),await to(t,r),r.text="Copying i18n files...",await ro(e,t),r.text="Configuring project structure...",await io(e),await so(e),await ao(e),await co(e),r.text="Migrating to [locale] structure...",await po(e),r.text="Installing dependencies...",await z(e,"next-intl");let o=Qt.join(e,c.VITEST_CONFIG);if(m(o)){let a=JSON.parse(await u(Qt.join(e,c.PACKAGE_JSON))),p={...a.dependencies,...a.devDependencies},d=!!(p["@reduxjs/toolkit"]&&p["react-redux"]);await re(e,{redux:d,i18n:!0})}r.text="Formatting code...";let s=await O(e);await U(e,s,"lint:fix"),r.succeed(la.green("Internationalization setup successfully!"))}catch(n){throw r.fail("Failed to setup Internationalization."),n}finally{await S(t)}};var lo={id:"i18n",name:"Internationalization",description:"next-intl with [locale] routing, RootProvider, and proxy.ts",detect:async e=>{let{detectProjectSetup:r}=await Promise.resolve().then(()=>(L(),Ne));return(await r(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:kn},{file:"src/providers/RootProvider.tsx",description:"<NextIntlClientProvider> wrap",presence:/NextIntlClientProvider/,removePattern:Se("NextIntlClientProvider")}],apply:ze};w();P();import ua from"node:path";w();import ma from"node:path";import da from"picocolors";P();var Ve=async e=>{let r=R("Enabling React Compiler...");try{let t=ma.join(e,c.NEXT_CONFIG);if(!m(t))throw new Error(`${c.NEXT_CONFIG} not found.`);let n=await u(t);if(n.includes("reactCompiler:")){r.fail("React Compiler is already configured in next.config.ts.");return}let i=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/;if(!i.test(n))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");n=n.replace(i,`$1
|
|
774
|
+
reactCompiler: true,`),await f(t,n),r.text="Installing babel-plugin-react-compiler...",await ot(e,"babel-plugin-react-compiler"),r.succeed(da.green("React Compiler enabled."))}catch(t){throw r.fail("Failed to enable React Compiler."),t}};var mo={id:"react-compiler",name:"React Compiler",description:"reactCompiler: true in next.config.ts + babel-plugin-react-compiler",detect:async e=>{let r=ua.join(e,c.NEXT_CONFIG);return m(r)?/reactCompiler\s*:\s*true/.test(await u(r)):!1},files:[],packages:[{name:"babel-plugin-react-compiler",kind:"devDependency"}],scripts:[],injections:[{file:c.NEXT_CONFIG,description:"`reactCompiler: true` flag",presence:/reactCompiler\s*:\s*true/,removePattern:/\n\s*reactCompiler:\s*true,?/}],apply:Ve};w();import rr from"node:path";import ga from"picocolors";P();w();P();import uo from"node:fs/promises";import he from"node:path";import fa from"degit";var fo=async(e,r)=>{r.text="Fetching assets from starter repo...",await fa("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},go=async(e,r)=>{let t=he.join(r,"src/providers/StoreProvider.tsx"),n=he.join(e,c.STORE_PROVIDER);await _(t,n);let i=he.join(r,"src/store"),o=he.join(e,c.STORE);await uo.cp(i,o,{recursive:!0})},ho=async(e,r)=>{let t=he.join(r,"src/features/counter"),n=he.join(e,c.COUNTER_FEATURE);await uo.cp(t,n,{recursive:!0});let i=he.join(n,"components/Counter.tsx");if(await m(i)){let o=await u(i);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 f(i,o)}};w();P();import er from"node:path";var xo=async e=>{let r=er.join(e,c.STORE),t=er.join(e,"package.json");if(m(r))return{isSetup:!0,reason:"src/store directory exists"};if(m(t)){let n=JSON.parse(await u(t));if(n.dependencies?.["@reduxjs/toolkit"]||n.devDependencies?.["@reduxjs/toolkit"])return{isSetup:!0,reason:"@reduxjs/toolkit is installed"}}return{isSetup:!1,reason:""}},yo=async e=>{let r=er.join(e,c.PROVIDERS_INDEX);if(!await dt(e)||!m(r))throw new Error("Project structure mismatch. Ensure src/app/layout.tsx (or src/app/[locale]/layout.tsx) and src/providers/index.ts exist.")};w();P();import tr from"node:path";var Po=async e=>{},vo=async e=>{let r=tr.join(e,"src/providers/RootProvider.tsx");if(!m(r))return;let t=await u(r),n="'use client';",i=t.includes(n);if(i&&(t=t.replace(n,"").trim()),t.includes("from '@/store'")||(t=`import type { AppState } from '@/store';
|
|
769
775
|
${t}`),t.includes("from './StoreProvider'")||(t=`${t.includes("from '@/store'"),""}import { StoreProvider } from './StoreProvider';
|
|
770
|
-
${t}`),
|
|
776
|
+
${t}`),i&&(t=`${n}
|
|
771
777
|
${t}`),t.includes("preloadedState")||(t=t.replace(/children:\s*React\.ReactNode;/,`children: React.ReactNode;
|
|
772
|
-
preloadedState?: Partial<AppState>;`),t=t.replace(/export const RootProvider = \(\{([\s\S]*?)\}:/,(
|
|
778
|
+
preloadedState?: Partial<AppState>;`),t=t.replace(/export const RootProvider = \(\{([\s\S]*?)\}:/,(o,s)=>`export const RootProvider = ({${s}, preloadedState }:`),t=t.replace(/,\s*,/g,",").replace(/\{\s*,/g,"{ ").replace(/,\s*\}/g," }")),!t.includes("<StoreProvider")){let o=t.match(/return\s*\(\s*([\s\S]*?)\s*\);/);if(o){let s=o[1];t=t.replace(o[0],`return (
|
|
773
779
|
<StoreProvider preloadedState={preloadedState}>
|
|
774
780
|
${s}
|
|
775
781
|
</StoreProvider>
|
|
776
|
-
);`)}}await f(r,t)},
|
|
777
|
-
${
|
|
782
|
+
);`)}}await f(r,t)},So=async e=>{let r=[tr.join(e,c.ROOT_PAGE),tr.join(e,"src/app/[locale]/page.tsx")],t="";for(let i of r)if(m(i)){t=i;break}if(!t)return;let n=await u(t);if(n.includes("Counter")||(n=`import { Counter } from '@/features/counter';
|
|
783
|
+
${n}`),!n.includes("<Counter />")){let i=n.lastIndexOf("</div>"),o=n.lastIndexOf("</main>"),s=o!==-1?o:i;s!==-1&&(n=n.slice(0,s)+`
|
|
778
784
|
<div className="mt-8">
|
|
779
785
|
<h2 className="mb-4 text-2xl font-bold">Redux Counter</h2>
|
|
780
786
|
<Counter />
|
|
781
787
|
</div>
|
|
782
|
-
`+
|
|
788
|
+
`+n.slice(s),await f(t,n))}};var Je=async e=>{let r=R("Setting up Redux Toolkit..."),t=rr.join(e,".next-maker-temp-redux");try{let{isSetup:n,reason:i}=await xo(e);if(n){r.fail(`Redux is already set up (${i}).`);return}await yo(e),await fo(t,r),r.text="Copying Redux files...",await go(e,t),r.text="Creating Counter feature...",await ho(e,t),r.text="Updating providers and pages...",await Po(e),await vo(e),await So(e),r.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=rr.join(e,c.VITEST_CONFIG);if(m(o)){let a=JSON.parse(await u(rr.join(e,c.PACKAGE_JSON))),p={...a.dependencies,...a.devDependencies};await re(e,{redux:!0,i18n:!!p["next-intl"]})}r.text="Formatting code...";let s=await O(e);await U(e,s,"lint:fix"),r.succeed(ga.green("Redux Toolkit setup successfully!"))}catch(n){throw r.fail("Failed to setup Redux Toolkit."),n}finally{await S(t)}};var wo={id:"redux",name:"Redux Toolkit",description:"Redux Toolkit + react-redux + redux-persist + StoreProvider",detect:async e=>{let{detectProjectSetup:r}=await Promise.resolve().then(()=>(L(),Ne));return(await r(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:Se("StoreProvider")}],apply:Je};w();P();import va from"node:path";w();import ya from"node:path";import Pa from"picocolors";P();var To=[{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"}],Co=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/,ha=/^\s+headers\s*:/m,xa=e=>`
|
|
783
789
|
headers: async () => {
|
|
784
790
|
return [
|
|
785
791
|
{
|
|
786
792
|
source: '/:path*',
|
|
787
793
|
headers: [
|
|
788
|
-
${e.map(({key:t,value:
|
|
794
|
+
${e.map(({key:t,value:n})=>` { key: '${t}', value: '${n}' },`).join(`
|
|
789
795
|
`)}
|
|
790
796
|
],
|
|
791
797
|
},
|
|
792
798
|
];
|
|
793
|
-
},`,
|
|
799
|
+
},`,we=e=>ha.test(e),nr=(e,r=To)=>{if(we(e))return e;if(!Co.test(e))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");return e.replace(Co,`$1${xa(r)}`)};var Be=async e=>{let r=R("Adding security headers to next.config.ts...");try{let t=ya.join(e,c.NEXT_CONFIG);if(!m(t))throw new Error(`${c.NEXT_CONFIG} not found.`);let n=await u(t);if(we(n)){r.succeed("Security headers already configured.");return}let i=nr(n);await f(t,i),r.succeed(Pa.green("Security headers added."))}catch(t){throw r.fail("Failed to add security headers."),t}};var Eo={id:"security-headers",name:"Security Headers",description:"Hardened HTTP headers in next.config.ts",detect:async e=>{let r=va.join(e,c.NEXT_CONFIG);return m(r)?we(await u(r)):!1},files:[],packages:[],scripts:[],injections:[{file:c.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:Be};w();P();import Ea from"node:path";w();import Sa from"node:fs/promises";import ae from"node:path";import wa from"degit";import Ca from"picocolors";P();var Ta=["vitest","jsdom","@vitejs/plugin-react","@testing-library/dom","@testing-library/jest-dom","@testing-library/react","@testing-library/user-event"],Xe=async e=>{let r=R("Setting up testing (Vitest + RTL)..."),t=ae.join(e,".next-maker-temp-tests");try{let n=ae.join(e,c.VITEST_CONFIG);if(m(n)){r.fail("Testing is already set up (vitest.config.ts exists).");return}r.text="Fetching assets from starter repo...",await wa("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(t),await _(ae.join(t,c.VITEST_CONFIG),ae.join(e,c.VITEST_CONFIG));let o=ae.join(e,c.TEST_DIR);await Sa.mkdir(o,{recursive:!0}),await _(ae.join(t,c.TEST_DIR,"setup.ts"),ae.join(o,"setup.ts"));let s=ae.join(e,c.PACKAGE_JSON),a=JSON.parse(await u(s)),p={...a.dependencies,...a.devDependencies},d=!!(p["@reduxjs/toolkit"]&&p["react-redux"]),g=!!p["next-intl"];await re(e,{redux:d,i18n:g}),await k(s,y=>(y.scripts=y.scripts??{},y.scripts.test=y.scripts.test??"vitest run",y.scripts["test:watch"]=y.scripts["test:watch"]??"vitest",y.scripts["test:coverage"]=y.scripts["test:coverage"]??"vitest run --coverage",typeof y.scripts.validate=="string"&&!/\byarn\s+test(?![:\w])/.test(y.scripts.validate)&&(y.scripts.validate=y.scripts.validate.replace(/(\byarn\s+check:deprecated\s*)(&&)/,"$1&& yarn test $2")),y)),r.text="Installing testing devDependencies...";let h=await O(e);await ue(e,h,Ta),r.text="Formatting code...",await U(e,h,"lint:fix"),r.succeed(Ca.green("Testing setup complete."))}catch(n){throw r.fail("Failed to set up testing."),n}finally{await S(t)}};var Ro={id:"tests",name:"Tests",description:"Vitest + React Testing Library + jsdom",detect:async e=>m(Ea.join(e,c.VITEST_CONFIG)),files:[{path:c.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:Xe};P();import Ia from"node:path";w();import $o from"node:path";import ba from"picocolors";P();var Ra={npm:"npm run",yarn:"yarn",pnpm:"pnpm",bun:"bun run"},$a=e=>{let r=Ra[e];return["ci:check","type-check","check:deprecated","test","build"].map(t=>`${r} ${t}`).join(" && ")},or=(e,r)=>{let t={...e.scripts??{}},n={...e.devDependencies??{}};return t["env:sync"]||(t["env:sync"]="tsx scripts/sync-env.ts"),t["check:deprecated"]||(t["check:deprecated"]="tsx scripts/check-deprecated.ts"),t["type-check"]||(t["type-check"]="tsc --noEmit"),t.validate||(t.validate=$a(r)),!n.tsx&&!e.dependencies?.hasOwnProperty("tsx")&&(n.tsx="*"),{...e,scripts:t,devDependencies:n}};var ir=`import * as fs from 'node:fs';
|
|
794
800
|
import * as path from 'node:path';
|
|
795
801
|
|
|
796
802
|
const envPath = path.join(process.cwd(), '.env');
|
|
@@ -856,7 +862,7 @@ if (currentExampleContent !== newExampleContent) {
|
|
|
856
862
|
} else {
|
|
857
863
|
console.log('\u2728 .env.example is already up to date');
|
|
858
864
|
}
|
|
859
|
-
`,
|
|
865
|
+
`,sr=`/**
|
|
860
866
|
* Walks every identifier in the project and flags any whose resolved TypeScript
|
|
861
867
|
* symbol carries a \`@deprecated\` JSDoc tag. Catches deprecated API usage that
|
|
862
868
|
* \`tsc --noEmit\` does not surface (deprecations are "suggestion"-level, not
|
|
@@ -920,114 +926,114 @@ if (hits === 0) {
|
|
|
920
926
|
|
|
921
927
|
console.log(\`\\n\${hits} deprecated symbol use(s) found.\`);
|
|
922
928
|
process.exit(1);
|
|
923
|
-
`;var
|
|
924
|
-
`)},
|
|
929
|
+
`;var Na=[{relative:"scripts/sync-env.ts",content:ir},{relative:"scripts/check-deprecated.ts",content:sr}],Ye=async e=>{let r=R("Adding validation scripts...");try{let t=!1;for(let{relative:o,content:s}of Na){let a=$o.join(e,o);m(a)||(await f(a,s),t=!0)}let n=await O(e),i=!1;await k($o.join(e,c.PACKAGE_JSON),o=>{let s=o.devDependencies?.tsx,a=or(o,n);return i=!s&&!!a.devDependencies?.tsx,a}),i&&(r.text="Installing tsx...",await ue(e,n,["tsx"])),r.succeed(ba.green(t?"Validation scripts added (env:sync, check:deprecated, validate).":"Validation scripts already present \u2014 refreshed package.json."))}catch(t){throw r.fail("Failed to add validation scripts."),t}};var bo=["scripts/sync-env.ts","scripts/check-deprecated.ts"],No={id:"validate-scripts",name:"Validation Scripts",description:"sync-env, check-deprecated, and the validate chain",detect:async e=>bo.every(r=>m(Ia.join(e,r))),files:bo.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:Ye};w();P();import Ce from"node:path";var Io=async e=>{let r=Ce.join(e,c.PACKAGE_JSON);return m(r)?JSON.parse(await u(r)):null},Aa=async(e,r)=>{let t=Ce.join(e,c.PACKAGE_JSON);await f(t,`${JSON.stringify(r,null,2)}
|
|
930
|
+
`)},Ao=(e,r)=>(r==="dependency"?e.dependencies:e.devDependencies)??{},Ke=async(e,r)=>{let t=await e.detect(r),n=[];if(!t)return{manifest:e,installed:!1,drift:[]};for(let o of e.files)o.required!==!1&&(m(Ce.join(r,o.path))||n.push({kind:"missingFile",file:o.path}));let i=await Io(r);if(i){for(let s of e.packages)Ao(i,s.kind)[s.name]||n.push({kind:"missingPackage",name:s.name,depKind:s.kind});let o=i.scripts??{};for(let s of e.scripts){let a=o[s.name];a?s.expectedValue&&a!==s.expectedValue&&n.push({kind:"mismatchedScript",name:s.name,expected:s.expectedValue,actual:a}):n.push({kind:"missingScript",name:s.name})}}for(let o of e.injections){let s=Ce.join(r,o.file);if(!m(s)){n.push({kind:"missingInjection",file:o.file,description:o.description});continue}let a=await u(s);o.presence.test(a)||n.push({kind:"missingInjection",file:o.file,description:o.description})}return{manifest:e,installed:t,drift:n}},xt=async(e,r,t={})=>{let n=t.dryRun??!1,i={filesRemoved:[],packagesUninstalled:[],scriptsRemoved:[],blocksStripped:[],manualCleanup:[]};for(let a of e.injections){let p=Ce.join(r,a.file);if(!m(p))continue;if(!a.removePattern){i.manualCleanup.push({file:a.file,description:a.description});continue}let d=await u(p);if(!a.presence.test(d))continue;let g;if(typeof a.removePattern=="function"){let h=a.removePattern(d);if(h===null){i.manualCleanup.push({file:a.file,description:a.description});continue}g=h}else g=d.replace(a.removePattern,"");g!==d?(n||await f(p,g),i.blocksStripped.push({file:a.file,description:a.description})):i.manualCleanup.push({file:a.file,description:a.description})}let{rm:o}=await import("node:fs/promises");for(let a of e.files){if(!a.generated)continue;let p=Ce.join(r,a.path);if(m(p)){if(a.containsUserContent){let d=a.removeHint?`${a.path} \u2014 ${a.removeHint}`:`${a.path} (may contain user-authored files; review before deleting)`;i.manualCleanup.push({file:a.path,description:d});continue}n||await o(p,{recursive:!0,force:!0}),i.filesRemoved.push(a.path)}}let s=await Io(r);if(s){let a=!1;if(s.scripts)for(let p of e.scripts)s.scripts[p.name]&&(n||delete s.scripts[p.name],i.scriptsRemoved.push(p.name),a=!0);for(let p of e.packages)Ao(s,p.kind)[p.name]&&i.packagesUninstalled.push(p.name);a&&!n&&await Aa(r,s)}if(!n&&i.packagesUninstalled.length>0){let a=await O(r);await Ct(r,a,i.packagesUninstalled)}return i};var xe=[Eo,No,jn,mo,_n,Ro,Xn,eo,wo,lo],ko=e=>xe.find(r=>r.id===e);var ka=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 ${j.dim(e.expected)} but got ${j.dim(e.actual)}`;case"missingInjection":return`missing block in ${e.file}: ${e.description}`}},Oa=e=>{let r=e.filter(n=>n.installed),t=r.filter(n=>n.drift.length>0);return{installedCount:r.length,driftedCount:t.length,cleanCount:r.length-t.length,driftedResults:t}},Oo=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 r=>{try{let t=process.cwd(),n=r.feature?xe.filter(s=>s.id===r.feature):xe;if(r.feature&&n.length===0)throw new Error(`Unknown feature "${r.feature}". Valid: ${xe.map(s=>s.id).join(", ")}.`);let i=[];for(let s of n)i.push(await Ke(s,t));r.json&&(l(JSON.stringify(i.map(s=>({id:s.manifest.id,name:s.manifest.name,installed:s.installed,drift:s.drift})),null,2)),process.exit(i.some(s=>s.drift.length>0)?1:0)),l(j.cyan(`
|
|
925
931
|
\u{1FA7A} Project Doctor
|
|
926
|
-
`));for(let s of
|
|
932
|
+
`));for(let s of i){let{manifest:a,installed:p,drift:d}=s;if(!p){l(` ${j.dim("\u2014")} ${j.dim(a.name)} ${j.dim("(not installed)")}`);continue}if(d.length===0){l(` ${j.green("\u2713")} ${a.name}`);continue}l(` ${j.yellow("!")} ${j.bold(a.name)} ${j.dim(`(${d.length} issue${d.length===1?"":"s"})`)}`);for(let g of d)l(` ${j.yellow("\u2022")} ${ka(g)}`)}let o=Oa(i);l(""),l(j.dim(`${o.cleanCount} clean, ${o.driftedCount} drifted, ${i.length-o.installedCount} not installed`)),o.driftedCount===0&&(l(j.green(`
|
|
927
933
|
\u2713 No drift detected.
|
|
928
934
|
`)),process.exit(0)),r.fix||(l(j.dim(`
|
|
929
935
|
Run with --fix to re-apply drifted features.
|
|
930
936
|
`)),process.exit(1)),l(j.cyan(`
|
|
931
937
|
\u{1F527} Applying fixes...
|
|
932
|
-
`));for(let s of
|
|
938
|
+
`));for(let s of o.driftedResults){if(!s.manifest.apply){l(j.yellow(` \u26A0\uFE0F ${s.manifest.name} has no apply() \u2014 skipping.`));continue}try{await s.manifest.apply(t)}catch(a){v(`Failed to fix ${s.manifest.name}: ${a}`)}}l(j.green(`
|
|
933
939
|
\u2713 Doctor finished.
|
|
934
|
-
`))}catch(t){v(`${t instanceof Error?t.message:t}`),process.exit(1)}})};import
|
|
935
|
-
.describe('${a}')`}return` ${r}: ${s},`},
|
|
940
|
+
`))}catch(t){v(`${t instanceof Error?t.message:t}`),process.exit(1)}})};import Te from"picocolors";w();P();import ar from"node:path";var _a=/^[A-Z][A-Z0-9_]*$/,Fo=e=>{if(!_a.test(e))throw new Error(`Invalid env var name "${e}". Use UPPER_SNAKE_CASE (e.g. SENTRY_DSN, NEXT_PUBLIC_API_URL).`)},ce=(e,r)=>r?e.startsWith("NEXT_PUBLIC_")?e:`NEXT_PUBLIC_${e}`:e,Fa=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,"\\'")}')`}},ja=e=>{switch(e.type){case"url":return"z.url()";case"number":return"z.coerce.number()";case"boolean":return"z.coerce.boolean()";case"enum":{let r=e.enumValues??[];if(r.length===0)throw new Error(`Enum env var "${e.name}" requires --enum values.`);return`z.enum([${r.map(n=>`'${n.replace(/'/g,"\\'")}'`).join(", ")}])`}default:return"z.string()"}},jo=e=>{let r=ce(e.name,e.public),n=ja(e),i=e.default!==void 0,o=!e.required&&!i;i&&(n+=Fa(e)),o&&e.type!=="enum"&&(n+=".optional()");let s;if(e.type==="enum"?s=n:s=`z.preprocess(emptyStringToUndefined, ${n})`,e.description){let a=e.description.replace(/'/g,"\\'");s+=`
|
|
941
|
+
.describe('${a}')`}return` ${r}: ${s},`},_o=/(export const envSchema\s*=\s*z\.object\(\{)([\s\S]*?)(\}\);?)/,Do=(e,r)=>{let t=ce(r.name,r.public);if(new RegExp(`\\b${t}\\s*:`).test(e))return e;let i=e.match(_o);if(!i)throw new Error("Could not locate `export const envSchema = z.object({ \u2026 })` in schema.ts.");let[,o,s,a]=i,p=s.replace(/\s+$/,""),d=p.length===0||/,\s*$/.test(p)?p:`${p},`,g=jo(r),h=`${d}
|
|
936
942
|
${g}
|
|
937
|
-
`;return e.replace(
|
|
943
|
+
`;return e.replace(_o,`${o}${h}${a}`)},Da="# -public",La=e=>{let r=ce(e.name,e.public),t=e.default??"",n=e.public?` ${Da}`:"";return`${r}=${t}${n}`},Lo=(e,r)=>{let t=ce(r.name,r.public);if(new RegExp(`^${t}=`,"m").test(e))return e;let i=e===""||e.endsWith(`
|
|
938
944
|
`)?"":`
|
|
939
|
-
`,
|
|
945
|
+
`,o=e===""?"":`
|
|
940
946
|
`,s=r.description?`# ${r.description}
|
|
941
|
-
`:"";return`${e}${
|
|
942
|
-
`},
|
|
947
|
+
`:"";return`${e}${i}${o}${s}${La(r)}
|
|
948
|
+
`},Mo=(e,r)=>{let t=ce(r.name,r.public);if(new RegExp(`^${t}=`,"m").test(e))return e;let i=e===""||e.endsWith(`
|
|
943
949
|
`)?"":`
|
|
944
|
-
`,
|
|
945
|
-
`},
|
|
950
|
+
`,o=r.default??"";return`${e}${i}${t}=${o}
|
|
951
|
+
`},cr=async(e,r)=>{Fo(r.name);let t=ar.join(e,"src/lib/env/schema.ts");if(!m(t))throw new Error("src/lib/env/schema.ts not found \u2014 run `next-maker init` first.");let n=await u(t),i=Do(n,r),o=i!==n;o&&await f(t,i);let s=ar.join(e,c.ENV_EXAMPLE),a=!1;if(m(s)){let g=await u(s),h=Lo(g,r);a=h!==g,a&&await f(s,h)}let p=ar.join(e,".env"),d=!1;if(m(p)){let g=await u(p),h=Mo(g,r);d=h!==g,d&&await f(p,h)}return{schemaUpdated:o,envExampleUpdated:a,envUpdated:d}};var Go=new Set(["string","url","number","boolean","enum"]),Ma=e=>{let r=(e??"string").toLowerCase();if(!Go.has(r))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(Go).join(", ")}.`);return r},Ga=e=>{if(!e)return;let r=e.split(",").map(t=>t.trim()).filter(Boolean);return r.length===0?void 0:r},Ho=e=>{e.command("env <NAME>").description("Declare a new environment variable across schema, .env.example, and .env").option("--type <type>","Zod 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 (auto-prefix NEXT_PUBLIC_ and 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(r,t)=>{try{let n=process.cwd();if(l(Te.cyan(`
|
|
946
952
|
\u{1F510} Env Var Generator
|
|
947
|
-
`)),t.required&&t.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let
|
|
953
|
+
`)),t.required&&t.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let i=Ma(t.type),o=Ga(t.enum);if(i==="enum"&&(!o||o.length===0))throw new Error("--type=enum requires at least one value via --enum a,b,c.");let s={name:r,type:i,required:!!t.required,default:t.default,description:t.describe,enumValues:o,public:!!t.public};x.start("Updating schema, .env.example, and .env...");let a=await cr(n,s);x.succeed("Env var written");let p=ce(s.name,s.public);l(Te.green(`
|
|
948
954
|
\u2728 ${p} declared.
|
|
949
|
-
`)),l(
|
|
950
|
-
`+e.slice(i
|
|
951
|
-
${e}`},
|
|
952
|
-
${r}: ${t},`,a=
|
|
953
|
-
})`)},
|
|
955
|
+
`)),l(Te.dim("Touched:")),l(Te.dim(` ${a.schemaUpdated?"\u270F\uFE0F ":"\u23ED "} src/lib/env/schema.ts`)),l(Te.dim(` ${a.envExampleUpdated?"\u270F\uFE0F ":"\u23ED "} .env.example`)),l(Te.dim(` ${a.envUpdated?"\u270F\uFE0F ":"\u23ED "} .env`)),l("")}catch(n){x.fail("Env var declaration failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import hr from"node:path";import D from"picocolors";L();L();import nc from"node:path";import{readFile as Ha,writeFile as Ua}from"node:fs/promises";import za from"node:path";var qe=(e,r)=>{let t=/import\s+.*\s+from\s+['"].*['"];?\n/g,n=e.match(t);if(n&&n.length>0){let i=n[n.length-1],o=e.lastIndexOf(i);return e.slice(0,o+i.length)+r+`
|
|
956
|
+
`+e.slice(o+i.length)}return`${r}
|
|
957
|
+
${e}`},pr=(e,r,t)=>{let n=/combineReducers\(\{([^}]*)\}\)/s,i=e.match(n);if(!i)throw new Error("Could not find combineReducers in rootReducer.ts");let o=i[1],s=`
|
|
958
|
+
${r}: ${t},`,a=o.trimEnd()+s;return e.replace(n,`combineReducers({${a}
|
|
959
|
+
})`)},We=(e,r)=>{let t=e.lastIndexOf("} as const;");if(t===-1)throw new Error("Could not find closing brace of AppApis object");let n=e.substring(0,t),i=e.substring(t);return`${n.trimEnd()}
|
|
954
960
|
${r}
|
|
955
|
-
${
|
|
961
|
+
${i}`};var Ze=async e=>{let{serviceName:r,projectPath:t}=e,n=A(r),i=za.join(t,"src","lib","config","app-apis.ts");try{let o=await Ha(i,"utf-8");if(o.includes(`${n}:`))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=` ${n}: {
|
|
956
962
|
base: \`\${API_PREFIX}/${r}\`,
|
|
957
963
|
getAll: \`\${API_PREFIX}/${r}\`,
|
|
958
|
-
},`;
|
|
959
|
-
`;
|
|
960
|
-
${
|
|
961
|
-
${
|
|
962
|
-
`),
|
|
963
|
-
${e.replace(/^\n+/,"")}`;let p=
|
|
964
|
-
`)},
|
|
965
|
-
`))};var
|
|
964
|
+
},`;o=We(o,s),await Ua(i,o)}catch(o){throw new Error(`Failed to register API endpoints: ${o}`,{cause:o})}};import{readFile as Va,writeFile as Ja}from"node:fs/promises";import Ba from"node:path";P();var lr=async(e,r,t)=>{let n=Ba.join(e,"src","lib","config","app-paths.ts");if(!m(n))return;let i=await Va(n,"utf-8"),o=A(r);if(i.includes(`${o}:`))return;let s=i.lastIndexOf("} as const;");if(s===-1)return;let a=` ${o}: '${t}',
|
|
965
|
+
`;i=i.slice(0,s)+a+i.slice(s),await Ja(n,i)};w();P();import yt from"node:path";var Xa=e=>new RegExp(`<\\s*${e}\\b`),Uo=(e,r)=>{if(Xa(r).test(e))return e;let t=/^(?<indent>[ \t]*)\{children\}\s*$/m,n=e.match(t);if(!n||n.index===void 0)throw new Error("Could not locate `{children}` line in RootProvider \u2014 expected a line containing only `{children}`.");let i=n.groups?.indent??"",o=`${i} `,s=`${i}<${r}>
|
|
966
|
+
${o}{children}
|
|
967
|
+
${i}</${r}>`;return e.replace(t,s)},zo=(e,r)=>{let t=`export * from './${r}';`;if(e.includes(t))return e;let n=e.split(`
|
|
968
|
+
`),i=[],o=[];for(let h of n)/^export \* from '\.\/.*';\s*$/.test(h)?i.push(h):o.push(h);let s=[...i,t].sort(),a=n.findIndex(h=>/^export \* from '\.\//.test(h));if(a===-1)return`${t}
|
|
969
|
+
${e.replace(/^\n+/,"")}`;let p=n.slice(0,a),d=n.findIndex((h,y)=>y>a&&!/^export \* from '\.\//.test(h)),g=d===-1?[]:n.slice(d);return[...p,...s,...g].join(`
|
|
970
|
+
`)},Ya=c.PROVIDERS,Ka=c.ROOT_PROVIDER,qa=c.PROVIDERS_INDEX,Vo=async(e,r={fileExists:m,readFile:u})=>{let t=yt.join(e,Ka);if(r.fileExists(t))return t;let n=yt.join(e,Ya);if(!r.fileExists(n))return null;let{readdir:i}=await import("node:fs/promises"),s=(await i(n).catch(()=>[])).filter(p=>p.endsWith(".tsx")),a=null;for(let p of s){let d=yt.join(n,p),g=await r.readFile(d);if(!/\{children\}/.test(g))continue;let y=((g.split("{children}")[0]??"").match(/<[A-Z][A-Za-z0-9_]*\b/g)??[]).length;(!a||y>a.depth)&&(a={file:d,depth:y})}return a?.file??null},mr=async e=>{let{projectPath:r,componentName:t,fileBaseName:n}=e,i=await Vo(r);if(i){let a=await u(i),p=a;(!/from '\.\//.test(a)||!a.includes(`{ ${t} }`))&&(p=qe(p,`import { ${t} } from './${n}';`)),p=Uo(p,t),p!==a&&await f(i,p)}let o=yt.join(r,qa),s=null;if(m(o)){let a=await u(o),p=zo(a,n);p!==a&&await f(o,p),s=o}return{rootProviderFile:i,barrelFile:s}};import{readFile as Wa,writeFile as Za}from"node:fs/promises";import Qa from"node:path";var Qe=async e=>{let{projectPath:r,name:t,persist:n,importPath:i}=e,o=Qa.join(r,"src","store","rootReducer.ts");try{let s=await Wa(o,"utf-8"),a=A(t),p=`${a}Reducer`,d=`${a}PersistConfig`,g=i.replace(/^src\//,"@/"),h=n?`import { ${p}, ${d} } from '${g}';`:`import { ${p} } from '${g}';`;s=qe(s,h);let y=n?`persistReducer(${d}, ${p})`:p;s=pr(s,a,y),await Za(o,s)}catch(s){throw new Error(`Failed to register reducer '${t}' in rootReducer: ${s}`,{cause:s})}};P();import{readFile as ec,writeFile as tc}from"node:fs/promises";import rc from"node:path";var dr=async(e,r)=>{let t=rc.join(e,"src","i18n","translations","en.json");if(!m(t))return;let n=await ec(t,"utf-8"),i=JSON.parse(n);i[r]||(i[r]={title:r,description:`${r} page description`},await tc(t,`${JSON.stringify(i,null,2)}
|
|
971
|
+
`))};var ur=()=>[{name:"Project setup detected",spinnerText:"Detecting project setup...",execute:async e=>{e.detection=await I(e.projectPath)}},{name:"Feature files generated",spinnerText:"Generating feature files...",execute:async e=>{await Mt({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 Ze({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 Qe({projectPath:e.projectPath,name:e.featureName,persist:e.persistStore,importPath:nc.join(e.basePath,e.featureName,"store")})}}];var ye=async(e,r)=>{for(let t of e)if(!t.shouldSkip?.(r)){r.spinner.start(t.spinnerText);try{await t.execute(r),r.spinner.succeed(t.name)}catch(n){throw r.spinner.fail(t.name),n}}};var fr=()=>[{name:"Service files generated",spinnerText:"Generating service files...",execute:async e=>{await Ie({name:e.serviceName,outputPath:e.servicePath,httpClient:e.httpClient})}},{name:"API endpoints registered",spinnerText:"Registering API endpoints...",execute:async e=>{await Ze({serviceName:e.serviceName,projectPath:e.projectPath})}}];import oc from"node:path";var gr=()=>[{name:"Slice files generated",spinnerText:"Generating slice files...",execute:async e=>{await Oe({name:e.sliceName,outputPath:e.slicePath,persist:e.persistSlice})}},{name:"Slice registered in rootReducer",spinnerText:"Registering slice in rootReducer...",execute:async e=>{await Qe({projectPath:e.projectPath,name:e.sliceName,persist:e.persistSlice,importPath:oc.join(e.basePath,e.sliceName)})}}];import ic from"enquirer";var{prompt:sc}=ic,Jo=async(e,r,t,n,i,o,s)=>{let a=[];e||a.push({type:"input",name:"featureName",message:"What is the feature name?",initial:"my-feature",validate:d=>/^[a-z0-9-]+$/.test(d)?!0:"Feature name must be lowercase and contain only alphanumeric characters and hyphens."}),r&&n===void 0&&i===void 0&&(a.push({type:"confirm",name:"createStore",message:"Generate Redux store for this feature?",initial:!0}),a.push({type:"confirm",name:"persistStore",message:"Enable persistence for this store?",initial:!1,skip(){return!this.state.answers.createStore}})),t&&t!=="none"&&o===void 0&&s===void 0&&(a.push({type:"confirm",name:"createService",message:"Generate API service for this feature?",initial:!0}),t==="both"&&a.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=a.length>0?await sc(a):{};return{featureName:e||p.featureName,hasRedux:r||!1,createStore:n===!0?!1:i!==void 0?!0:p.createStore||!1,persistStore:i==="persist"?!0:i==="no-persist"?!1:p.persistStore||!1,httpClient:t||"none",createService:o===!0?!1:s!==void 0?!0:p.createService||!1,selectedHttpClient:s||p.selectedHttpClient||(t==="both"?"fetch":t!=="none"?t:void 0)}};var Bo=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(r,t)=>{try{let n=process.cwd();ac(t),l(D.cyan(`
|
|
966
972
|
\u{1F3AF} Feature Generator
|
|
967
|
-
`));let
|
|
968
|
-
`));let a=await
|
|
973
|
+
`));let i=ur();x.start("Detecting project setup...");let{detectProjectSetup:o}=await Promise.resolve().then(()=>(L(),Ne)),s=await o(n);x.succeed("Project setup detected"),l(D.dim(` Redux: ${s.hasRedux?"\u2713":"\u2717"}`)),l(D.dim(` HTTP Client: ${s.httpClient}`)),l(D.dim(` i18n: ${s.hasI18n?"\u2713":"\u2717"}
|
|
974
|
+
`));let a=await Jo(r,s.hasRedux,s.httpClient,t.skipStore,t.store,t.skipService,t.service),p=t.path||hr.join("src","features"),d=hr.join(n,p,a.featureName);await ie(n,a.featureName,p)&&(v(`Feature '${a.featureName}' already exists at ${p}!`),process.exit(1)),await ye(i.slice(1),{projectPath:n,spinner:x,featureName:a.featureName,basePath:p,featurePath:d,createStore:a.createStore,persistStore:a.persistStore,createService:a.createService,httpClient:a.selectedHttpClient,detection:s}),cc(a,p)}catch(n){x.fail("Feature generation failed"),v(`${n}`),process.exit(1)}})},ac=e=>{e.store&&!["persist","no-persist"].includes(e.store)&&(v("Invalid --store option. Use: persist or no-persist"),process.exit(1)),e.service&&!["axios","fetch"].includes(e.service)&&(v("Invalid --service option. Use: axios or fetch"),process.exit(1)),e.skipStore&&e.store&&(v("Cannot use --skip-store and --store together"),process.exit(1)),e.skipService&&e.service&&(v("Cannot use --skip-service and --service together"),process.exit(1))},cc=(e,r)=>{let t=hr.join(r,e.featureName);l(D.green(`
|
|
969
975
|
\u2728 Feature '${e.featureName}' created successfully!
|
|
970
976
|
`)),l(D.dim("Generated files:")),l(D.dim(` \u{1F4C2} ${t}/`)),l(D.dim(" \u251C\u2500\u2500 components/")),l(D.dim(" \u251C\u2500\u2500 hooks/")),l(D.dim(" \u251C\u2500\u2500 types/")),e.createStore&&l(D.dim(" \u251C\u2500\u2500 store/")),e.createService&&l(D.dim(" \u251C\u2500\u2500 services/")),l(D.dim(` \u2514\u2500\u2500 index.ts
|
|
971
|
-
`)),l(D.cyan("Next steps:"));let
|
|
977
|
+
`)),l(D.cyan("Next steps:"));let n=r.replace(/^src\//,"@/");l(D.dim(` 1. Import and use the feature: import { ${e.featureName} } from '${n}/${e.featureName}'`)),e.createStore&&l(D.dim(` 2. Customize your Redux slice in: ${t}/store/`)),e.createService&&l(D.dim(` 3. Add API methods in: ${t}/services/${e.featureName}.service.ts`)),l("")};import xr from"node:path";import Ee from"picocolors";L();import pc from"enquirer";var{prompt:lc}=pc,Xo=async e=>e?{hookName:e}:await lc({type:"input",name:"hookName",message:'Hook name (kebab-case, without "use" prefix):',validate:t=>t?/^[a-z][a-z0-9-]*$/.test(t)?!0:"Use lowercase letters, numbers, and hyphens only":"Hook name is required"});var Yo=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(r,t)=>{try{let n=process.cwd();l(Ee.cyan(`
|
|
972
978
|
\u{1FA9D} Hook Generator
|
|
973
|
-
`));let
|
|
979
|
+
`));let i=await I(n),o=await Xo(r),a=`use${$(o.hookName)}`,p=mc(t,i.hasTests);x.start("Generating hook..."),await Ht({name:o.hookName,projectPath:n,isClient:t.client??!0,featurePath:t.feature}),x.succeed("Hook generated");let d=t.feature?xr.join(n,t.feature,"hooks",`${a}.ts`):xr.join(n,"src/hooks",`${a}.ts`),g=null;p&&(x.start("Generating test..."),g=await Q({projectPath:n,sourceFile:d,kind:"hook",symbolName:a,hookUsesStore:i.hasRedux&&!!t.feature}),x.succeed("Test generated"));let h=t.feature?`${t.feature}/hooks/${a}.ts`:`src/hooks/${a}.ts`;l(Ee.green(`
|
|
974
980
|
\u2728 Hook '${a}' created successfully!
|
|
975
|
-
`)),l(
|
|
981
|
+
`)),l(Ee.dim(` \u{1F4C4} ${h}`)),g&&l(Ee.dim(` \u{1F9EA} ${xr.relative(n,g)}`)),l(""),l(Ee.cyan("Usage:"));let y=t.feature?`${t.feature.replace(/^src\//,"@/")}/hooks/${a}`:`@/hooks/${a}`;l(Ee.dim(` import { ${a} } from '${y}';`)),l("")}catch(n){x.fail("Hook generation failed"),v(`${n}`),process.exit(1)}})},mc=(e,r)=>e.noTest?!1:e.test?!0:r;import Pt from"picocolors";L();var Ko=e=>{e.command("layout <segment>").description("Generate a nested layout.tsx (locale-aware when i18n is detected)").option("--group","Treat the segment as a route group (wraps it in parens)").option("--at <path>","Place the layout under this nested path (kebab-case, slash-separated)").option("--no-locale","Skip the locale wrapper even if i18n is detected").action(async(r,t)=>{try{let n=process.cwd();l(Pt.cyan(`
|
|
976
982
|
\u{1F4D0} Layout Generator
|
|
977
|
-
`)),
|
|
978
|
-
`)),
|
|
983
|
+
`)),x.start("Detecting project setup...");let i=await I(n);x.succeed("Project setup detected");let o=i.hasI18n&&t.locale!==!1;l(Pt.dim(` i18n: ${o?"\u2713":"\u2717"}
|
|
984
|
+
`)),x.start("Generating layout...");let s=await Ut({segment:r,at:t.at,projectPath:n,hasI18n:o,withGroup:!!t.group});x.succeed("Layout generated"),l(Pt.green(`
|
|
979
985
|
\u2728 ${s.componentName} created!
|
|
980
|
-
`)),l(
|
|
986
|
+
`)),l(Pt.dim(` \u{1F4C4} ${s.displayPath}`)),l("")}catch(n){x.fail("Layout generation failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import fc from"node:path";import W from"picocolors";P();L();import dc from"enquirer";var{prompt:uc}=dc,qo=async e=>{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"},{type:"confirm",name:"copyTranslations",message:"Copy translations from English? (No = empty values)",initial:!1});let t=await uc(r);return{code:e||t.code,name:t.name,country:t.country,flag:t.flag,copyTranslations:t.copyTranslations}};var Wo=e=>{e.command("locale [code]").description("Add a new locale/language").option("--copy-translations","Copy translations from English instead of empty values").action(async(r,t)=>{try{let n=process.cwd();l(W.cyan(`
|
|
981
987
|
\u{1F30D} Locale Generator
|
|
982
|
-
`)),
|
|
988
|
+
`)),x.start("Detecting project setup...");let i=await I(n);x.succeed("Project setup detected"),i.hasI18n||(x.fail("i18n is not set up in this project"),v("Please set up internationalization first"),l(W.dim(`
|
|
983
989
|
Run: npx @teispace/next-maker setup --i18n
|
|
984
|
-
`)),process.exit(1));let
|
|
985
|
-
\u2728 Locale '${
|
|
986
|
-
`)),l(W.dim("Updated files:")),l(W.dim(` \u{1F4C4} src/i18n/translations/${
|
|
990
|
+
`)),process.exit(1));let o=await qo(r),s=fc.join(n,"src","i18n","translations",`${o.code}.json`);m(s)&&(v(`Locale '${o.code}' already exists!`),process.exit(1)),x.start("Adding locale..."),await zt({...o,projectPath:n,copyTranslations:t.copyTranslations??o.copyTranslations}),x.succeed("Locale added"),l(W.green(`
|
|
991
|
+
\u2728 Locale '${o.code}' (${o.name}) added successfully!
|
|
992
|
+
`)),l(W.dim("Updated files:")),l(W.dim(` \u{1F4C4} src/i18n/translations/${o.code}.json`)),l(W.dim(" \u{1F4DD} src/types/i18n.ts (SupportedLocale type)")),l(W.dim(" \u{1F4DD} src/lib/config/app-locales.ts")),l(""),l(W.cyan("Next steps:")),l(W.dim(` 1. Translate: src/i18n/translations/${o.code}.json`)),l(W.dim(` 2. Visit: http://localhost:3000/${o.code}`)),l("")}catch(n){x.fail("Locale generation failed"),v(`${n}`),process.exit(1)}})};import Qo from"node:path";import J from"picocolors";L();import gc from"enquirer";var{prompt:hc}=gc,Zo=async e=>{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"}),r.push({type:"confirm",name:"withLoading",message:"Generate loading.tsx?",initial:!0},{type:"confirm",name:"withError",message:"Generate error.tsx?",initial:!0});let t=await hc(r);return{pageName:e||t.pageName,withLoading:t.withLoading,withError:t.withError}};var ei=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(r,t)=>{try{let n=process.cwd();l(J.cyan(`
|
|
987
993
|
\u{1F4C4} Page Generator
|
|
988
|
-
`)),
|
|
989
|
-
`));let
|
|
994
|
+
`)),x.start("Detecting project setup...");let i=await I(n);x.succeed("Project setup detected"),l(J.dim(` i18n: ${i.hasI18n?"\u2713":"\u2717"}
|
|
995
|
+
`));let o=await Zo(r),s=o.pageName,a=$(s),p=i.hasI18n?"src/app/[locale]":"src/app";await ie(n,s,p)&&(v(`Page '${s}' already exists at ${p}/${s}!`),process.exit(1)),x.start("Generating page files..."),await Jt({name:s,projectPath:n,hasI18n:i.hasI18n,dynamic:t.dynamic,withLoading:t.loading??o.withLoading,withError:t.error??o.withError}),x.succeed("Page files generated"),x.start("Registering route...");let g=t.dynamic?`/${s}/[${t.dynamic}]`:`/${s}`;await lr(n,s,g),x.succeed("Route registered"),i.hasI18n&&(x.start("Adding translation namespace..."),await dr(n,a),x.succeed("Translation namespace added"));let h=t.dynamic?Qo.join(p,s,`[${t.dynamic}]`):Qo.join(p,s);l(J.green(`
|
|
990
996
|
\u2728 Page '${s}' created successfully!
|
|
991
|
-
`)),l(J.dim("Generated files:")),l(J.dim(` \u{1F4C2} ${
|
|
997
|
+
`)),l(J.dim("Generated files:")),l(J.dim(` \u{1F4C2} ${h}/`)),l(J.dim(" \u251C\u2500\u2500 page.tsx")),(t.loading??o.withLoading)&&l(J.dim(" \u251C\u2500\u2500 loading.tsx")),(t.error??o.withError)&&l(J.dim(" \u2514\u2500\u2500 error.tsx")),l(""),i.hasI18n&&(l(J.cyan("Next steps:")),l(J.dim(` 1. Add translations in: src/i18n/translations/en.json \u2192 "${a}"`)),l(J.dim(` 2. Visit: http://localhost:3000/${s}`)),l(""))}catch(n){x.fail("Page generation failed"),v(`${n}`),process.exit(1)}})};import yr from"node:path";import pe from"picocolors";var ti=e=>{e.command("provider <name>").description("Generate a context provider in src/providers/ and wire it into RootProvider").action(async r=>{try{let t=process.cwd();l(pe.cyan(`
|
|
992
998
|
\u{1F50C} Provider Generator
|
|
993
|
-
`)),
|
|
994
|
-
\u2728 ${
|
|
995
|
-
`)),l(pe.dim("Generated files:")),l(pe.dim(` \u{1F4C4} ${
|
|
996
|
-
\u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),l(pe.dim(` <${
|
|
997
|
-
\u{1F5D1} Remove ${
|
|
998
|
-
`)),!(await
|
|
999
|
+
`)),x.start("Generating provider...");let{componentName:n,fileBaseName:i,providerFile:o}=await Xt({name:r,projectPath:t});x.succeed("Provider generated"),x.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:s,barrelFile:a}=await mr({projectPath:t,componentName:n,fileBaseName:i});x.succeed("Provider registered"),l(pe.green(`
|
|
1000
|
+
\u2728 ${n} created!
|
|
1001
|
+
`)),l(pe.dim("Generated files:")),l(pe.dim(` \u{1F4C4} ${yr.relative(t,o)}`)),s?l(pe.dim(` \u270F\uFE0F ${yr.relative(t,s)} \u2014 wrapped {children}`)):(l(pe.yellow(`
|
|
1002
|
+
\u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),l(pe.dim(` <${n}>{children}</${n}>`))),a&&l(pe.dim(` \u270F\uFE0F ${yr.relative(t,a)} \u2014 added re-export`)),l("")}catch(t){x.fail("Provider generation failed"),v(`${t instanceof Error?t.message:t}`),process.exit(1)}})};import xc from"enquirer";import M from"picocolors";var{prompt:yc}=xc,Pc=e=>{let r=[];for(let t of e.filesRemoved)r.push(` ${M.red("-")} delete: ${t}`);for(let t of e.blocksStripped)r.push(` ${M.red("-")} strip block: ${t.description} ${M.dim(`(${t.file})`)}`);for(let t of e.scriptsRemoved)r.push(` ${M.red("-")} remove script: ${t}`);for(let t of e.packagesUninstalled)r.push(` ${M.red("-")} uninstall: ${t}`);for(let t of e.manualCleanup)r.push(` ${M.yellow("?")} manual cleanup: ${t.description} ${M.dim(`(${t.file})`)}`);return r},vc=e=>e.filesRemoved.length===0&&e.blocksStripped.length===0&&e.scriptsRemoved.length===0&&e.packagesUninstalled.length===0&&e.manualCleanup.length===0,ri=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(r,t)=>{try{let n=process.cwd(),i=ko(r);if(!i)throw new Error(`Unknown feature "${r}". Valid: ${xe.map(p=>p.id).join(", ")}.`);if(l(M.cyan(`
|
|
1003
|
+
\u{1F5D1} Remove ${i.name}
|
|
1004
|
+
`)),!(await Ke(i,n)).installed){l(M.dim(`${i.name} is not installed \u2014 nothing to remove.`));return}x.start("Computing changes...");let s=await xt(i,n,{dryRun:!0});if(x.succeed("Computed plan"),vc(s)){l(M.yellow(`
|
|
999
1005
|
\u26A0\uFE0F Manifest had no removable artifacts. Nothing to do.
|
|
1000
1006
|
`));return}l(M.bold(`
|
|
1001
1007
|
Planned changes:
|
|
1002
|
-
`));for(let p of
|
|
1003
|
-
`));return}if(!t.yes){let{confirm:p}=await
|
|
1008
|
+
`));for(let p of Pc(s))l(p);if(l(""),t.dryRun){l(M.dim(`--dry-run set \u2014 exiting without writing.
|
|
1009
|
+
`));return}if(!t.yes){let{confirm:p}=await yc({type:"confirm",name:"confirm",message:`Apply the changes above to remove ${i.name}?`,initial:!1});if(!p){l(M.yellow(`
|
|
1004
1010
|
Aborted.
|
|
1005
|
-
`));return}}
|
|
1006
|
-
\u2713 ${
|
|
1007
|
-
\u26A0\uFE0F Manual cleanup still required:`));for(let p of a.manualCleanup)l(` - ${p.description} (${p.file})`)}l("")}catch(
|
|
1011
|
+
`));return}}x.start(`Removing ${i.name}...`);let a=i.remove?(await i.remove(n),s):await xt(i,n);if(x.succeed(`${i.name} removed`),l(M.green(`
|
|
1012
|
+
\u2713 ${i.name} removed.`)),a.manualCleanup.length>0){l(M.yellow(`
|
|
1013
|
+
\u26A0\uFE0F Manual cleanup still required:`));for(let p of a.manualCleanup)l(` - ${p.description} (${p.file})`)}l("")}catch(n){x.fail("Remove failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import{existsSync as Tc}from"node:fs";import{mkdir as Ec}from"node:fs/promises";import et from"node:path";import B from"picocolors";L();import{readFile as Sc,writeFile as wc}from"node:fs/promises";import Cc from"node:path";var ni=async(e,r)=>{let t=A(r),n=Cc.join(e,"src","lib","config","app-apis.ts");try{let i=await Sc(n,"utf-8");if(i.includes(`${t}:`))return;if(!i.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let o=sn({camelName:t,kebabName:r});i=We(i,o),await wc(n,i)}catch(i){throw new Error(`Failed to register CRUD API endpoints: ${i}`,{cause:i})}};import oi from"enquirer";var ii=async(e,r,t,n)=>{let i=e||(await oi.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 r!==void 0?o="axios":t!==void 0?o="fetch":n==="axios"?o="axios":n==="fetch"?o="fetch":n==="both"?o=(await oi.prompt({type:"select",name:"httpClient",message:"Choose HTTP client:",choices:["axios","fetch"]})).httpClient:o="axios",{serviceName:i,httpClient:o}};var si=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(r,t)=>{try{let n=process.cwd();t.axios&&t.fetch&&(v("Cannot use --axios and --fetch together"),process.exit(1)),l(B.cyan(`
|
|
1008
1014
|
\u{1F527} Service Generator
|
|
1009
|
-
`)),
|
|
1015
|
+
`)),x.start("Detecting project setup...");let i=await I(n);x.succeed("Project setup detected"),i.httpClient==="none"&&(x.fail("No HTTP client is setup in this project"),v("Please setup either AxiosClient or FetchClient first"),l(B.dim(`
|
|
1010
1016
|
Check: lib/utils/http/axios-client or lib/utils/http/fetch-client
|
|
1011
|
-
`)),process.exit(1));let
|
|
1012
|
-
`));let s=await
|
|
1017
|
+
`)),process.exit(1));let o=[];(i.httpClient==="axios"||i.httpClient==="both")&&o.push("Axios \u2713"),(i.httpClient==="fetch"||i.httpClient==="both")&&o.push("Fetch \u2713"),l(B.dim(` HTTP Clients: ${o.join(", ")}
|
|
1018
|
+
`));let s=await ii(r,t.axios,t.fetch,i.httpClient),{basePath:a,servicePath:p}=Rc(n,s.serviceName,t.path);Tc(p)||await Ec(p,{recursive:!0}),at(n,a,`${s.serviceName}.service.ts`)&&(v(`Service '${s.serviceName}' already exists at ${a}!`),process.exit(1)),t.crud?(x.start("Generating CRUD service files..."),await Lt({name:s.serviceName,outputPath:p,httpClient:s.httpClient}),x.succeed("CRUD service files generated"),x.start("Registering CRUD API endpoints..."),await ni(n,s.serviceName),x.succeed("CRUD API endpoints registered")):await ye(fr(),{projectPath:n,spinner:x,serviceName:s.serviceName,servicePath:p,httpClient:s.httpClient}),$c(s,a,!!t.crud)}catch(n){x.fail("Service generation failed"),v(`${n}`),process.exit(1)}})},Rc=(e,r,t)=>{let n;if(t){let i=t.replace(/^src\//,"");if(i.startsWith("features/")){let o=i.split("/")[1];n=et.join("src","features",o,"services")}else n=et.join("src",i)}else n=et.join("src","features",r,"services");return{basePath:n,servicePath:et.join(e,n)}},$c=(e,r,t=!1)=>{let n=et.join(r,`${e.serviceName}.service.ts`);l(B.green(`
|
|
1013
1019
|
\u2728 ${t?"CRUD ":""}Service '${e.serviceName}' created successfully!
|
|
1014
|
-
`)),l(B.dim("Generated files:")),l(B.dim(` \u{1F4C4} ${
|
|
1015
|
-
`)),l(B.cyan("Next steps:"));let
|
|
1020
|
+
`)),l(B.dim("Generated files:")),l(B.dim(` \u{1F4C4} ${n}
|
|
1021
|
+
`)),l(B.cyan("Next steps:"));let i=r.replace(/^src\//,"@/");l(B.dim(` 1. Import service: import { ${e.serviceName}Service } from '${i}/${e.serviceName}.service'`)),t?(l(B.dim(" 2. Available methods: getAll, getById, create, update, delete")),l(B.dim(" 3. Update DTO types in the service file"))):l(B.dim(` 2. Use in component: const data = await ${e.serviceName}Service.getAll()`)),l("")};import bc from"enquirer";import Pr from"picocolors";var{prompt:Nc}=bc,ai=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("--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 r=>{try{if(l(Pr.cyan(`
|
|
1016
1022
|
\u{1F527} Setup Wizard
|
|
1017
|
-
`)),r.httpClient||r.darkTheme||r.redux||r.i18n||r.tests||r.reactCompiler||r.bundleAnalyzer||r.securityHeaders||r.validateScripts||r.commitizen){r.httpClient&&await
|
|
1018
|
-
\u26A0\uFE0F ${
|
|
1023
|
+
`)),r.httpClient||r.darkTheme||r.redux||r.i18n||r.tests||r.reactCompiler||r.bundleAnalyzer||r.securityHeaders||r.validateScripts||r.commitizen){r.httpClient&&await Ue(process.cwd()),r.darkTheme&&await Ge(process.cwd()),r.redux&&await Je(process.cwd()),r.i18n&&await ze(process.cwd()),r.tests&&await Xe(process.cwd()),r.reactCompiler&&await Ve(process.cwd()),r.bundleAnalyzer&&await De(process.cwd()),r.securityHeaders&&await Be(process.cwd()),r.validateScripts&&await Ye(process.cwd()),r.commitizen&&await Le(process.cwd());return}let i=(await Nc([{type:"select",name:"feature",message:"What would you like to setup?",choices:[{name:"Dark Theme",value:"dark-theme"},{name:"Redux Toolkit",value:"redux"},{name:"HTTP Client (Axios/Fetch)",value:"http-client"},{name:"Internationalization (next-intl)",value:"i18n"},{name:"Testing (Vitest + RTL)",value:"tests"},{name:"React Compiler",value:"react-compiler"},{name:"Bundle Analyzer",value:"bundle-analyzer"},{name:"Security Headers",value:"security-headers"},{name:"Validation Scripts",value:"validate-scripts"},{name:"Commitizen",value:"commitizen"},"Cancel"]}])).feature;if(i==="Cancel"){l(Pr.yellow("Setup cancelled."));return}let s={"dark-theme":Ge,redux:Je,"http-client":Ue,i18n:ze,tests:Xe,"react-compiler":Ve,"bundle-analyzer":De,"security-headers":Be,"validate-scripts":Ye,commitizen:Le}[i];s?await s(process.cwd()):l(Pr.yellow(`
|
|
1024
|
+
\u26A0\uFE0F ${i} setup is not implemented yet.`))}catch(t){x.fail("Setup failed"),v(`${t}`),process.exit(1)}})};import{existsSync as kc}from"node:fs";import{mkdir as Oc}from"node:fs/promises";import le from"node:path";import G from"picocolors";L();import Ic from"enquirer";var{prompt:Ac}=Ic,ci=async(e,r,t)=>{let n=[];e||n.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."}),r===void 0&&t===void 0&&n.push({type:"confirm",name:"persistSlice",message:"Enable persistence for this slice?",initial:!1});let i=n.length>0?await Ac(n):{};return{sliceName:e||i.sliceName,persistSlice:r===!0?!0:t===!1?!1:i.persistSlice||!1}};var pi=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(r,t)=>{try{let n=process.cwd();t.persist&&t.noPersist===!0&&(v("Cannot use --persist and --no-persist together"),process.exit(1)),l(G.cyan(`
|
|
1019
1025
|
\u{1F527} Slice Generator
|
|
1020
|
-
`)),
|
|
1026
|
+
`)),x.start("Detecting project setup...");let i=await I(n);x.succeed("Project setup detected"),i.hasRedux||(x.fail("Redux is not setup in this project"),v("Please install @reduxjs/toolkit and react-redux first"),l(G.dim(`
|
|
1021
1027
|
Run: npm install @reduxjs/toolkit react-redux
|
|
1022
1028
|
`)),process.exit(1)),l(G.dim(` Redux: \u2713
|
|
1023
|
-
`));let
|
|
1029
|
+
`));let o=await ci(r,t.persist,t.noPersist===!0?!1:void 0),{basePath:s,slicePath:a}=_c(n,o.sliceName,t.path),p=le.join(n,s);kc(p)||await Oc(p,{recursive:!0}),await ie(n,o.sliceName,s)&&(v(`Slice '${o.sliceName}' already exists at ${s}!`),process.exit(1)),await ye(gr(),{projectPath:n,spinner:x,sliceName:o.sliceName,basePath:s,slicePath:a,persistSlice:o.persistSlice});let g=null;if(Fc(t,i.hasTests)){let y=le.join(a,`${o.sliceName}.slice.ts`);x.start("Generating test..."),g=await Q({projectPath:n,sourceFile:y,kind:"slice",symbolName:A(o.sliceName)}),x.succeed("Test generated")}jc(o,s,g,n)}catch(n){x.fail("Slice generation failed"),v(`${n}`),process.exit(1)}})},_c=(e,r,t)=>{let n;if(t){let i=t.replace(/^src\//,"");if(i.startsWith("features/")){let o=i.split("/")[1];n=le.join("src","features",o,"store")}else n=le.join("src",i)}else n=le.join("src","features",r,"store");return{basePath:n,slicePath:le.join(e,n,r)}},Fc=(e,r)=>e.noTest?!1:e.test?!0:r,jc=(e,r,t,n)=>{let i=le.join(r,e.sliceName);l(G.green(`
|
|
1024
1030
|
\u2728 Slice '${e.sliceName}' created successfully!
|
|
1025
|
-
`)),l(G.dim("Generated files:")),l(G.dim(` \u{1F4C2} ${
|
|
1026
|
-
`)),t&&l(G.dim(` \u{1F9EA} ${le.relative(
|
|
1027
|
-
`)),l(G.cyan("Next steps:"));let
|
|
1028
|
-
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(r,t)=>{try{let
|
|
1031
|
+
`)),l(G.dim("Generated files:")),l(G.dim(` \u{1F4C2} ${i}/`)),l(G.dim(` \u251C\u2500\u2500 ${e.sliceName}.slice.ts`)),l(G.dim(` \u251C\u2500\u2500 ${e.sliceName}.selectors.ts`)),e.persistSlice&&l(G.dim(" \u251C\u2500\u2500 persist.ts")),l(G.dim(` \u251C\u2500\u2500 ${e.sliceName}.types.ts`)),l(G.dim(` \u2514\u2500\u2500 index.ts
|
|
1032
|
+
`)),t&&l(G.dim(` \u{1F9EA} ${le.relative(n,t)}
|
|
1033
|
+
`)),l(G.cyan("Next steps:"));let o=r.replace(/^src\//,"@/");l(G.dim(` 1. Import actions: import { setLoading, setError } from '${o}/${e.sliceName}'`)),l(G.dim(" 2. Use in component: dispatch(setLoading(true))")),l("")};import X from"node:path";import vr from"picocolors";P();L();var li=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
|
|
1034
|
+
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(r,t)=>{try{let n=process.cwd(),i=X.isAbsolute(r)?r:X.join(n,r);l(vr.cyan(`
|
|
1029
1035
|
\u{1F9EA} Test Generator
|
|
1030
|
-
`)),m(
|
|
1036
|
+
`)),m(i)||(v(`Source file not found: ${r}`),process.exit(1));let o=await I(n);o.hasTests||(v("Testing is not installed in this project. Run `next-maker setup --tests` first."),process.exit(1));let s=await u(i),a=Dc(t.kind)??Lc(i,s);a||(v(`Could not infer test kind for ${X.basename(i)}. Pass --kind component|hook|slice.`),process.exit(1));let p=Mc(i,a),d=Gc(i,a);m(d)&&!t.force&&(v(`Test file already exists: ${X.relative(n,d)}. Use --force to overwrite.`),process.exit(1)),x.start(`Generating ${a} test...`);let g=await Q({projectPath:n,sourceFile:i,kind:a,symbolName:p,hasRedux:o.hasRedux,hasI18n:o.hasI18n,hookUsesStore:a==="hook"&&o.hasRedux&&s.includes("useAppSelector")});x.succeed("Test generated"),l(vr.green(`
|
|
1031
1037
|
\u2728 Test '${X.basename(g)}' created.
|
|
1032
|
-
`)),l(
|
|
1038
|
+
`)),l(vr.dim(` \u{1F9EA} ${X.relative(n,g)}`)),l("")}catch(n){x.fail("Test generation failed"),v(`${n}`),process.exit(1)}})},Dc=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},Lc=(e,r)=>{let t=X.basename(e);return/\.slice\.ts$/.test(t)?"slice":/^use[A-Z]/.test(t.replace(/\.tsx?$/,""))?"hook":t.endsWith(".tsx")?"component":/createSlice\s*\(/.test(r)?"slice":/^export\s+(const|function)\s+use[A-Z]/m.test(r)?"hook":null},Mc=(e,r)=>{let t=X.basename(e).replace(/\.(tsx?|jsx?)$/i,"");return r==="slice"?A(t.replace(/\.slice$/,"")):r==="hook"?/^use[A-Z]/.test(t)?t:`use${$(t.replace(/^use-?/,""))}`:$(t)},Gc=(e,r)=>{let t=X.dirname(e),n=X.basename(e).replace(/\.(tsx?|jsx?)$/i,""),i=r==="component"?".tsx":".ts";return X.join(t,`${n}.test${i}`)};var mi=e=>{Zr(e),ai(e),Bo(e),pi(e),si(e),ei(e),Ko(e),In(e),Wo(e),Yo(e),ti(e),Ho(e),Oo(e),ri(e),li(e)};var Uc=process.emitWarning;process.emitWarning=function(e,r,t,n){if(!(typeof e=="string"&&e.includes("--localstorage-file")&&e.includes("was provided without a valid path")))return Uc.apply(process,arguments)};async function zc(){wr({logger:r=>nt(r)});let e=new Hc;e.name("next-maker").description("Teispace Next.js Project Generator").version("1.0.0"),mi(e),e.parse()}zc().catch(e=>{let r=e&&typeof e=="object"&&"message"in e?e.message:String(e);nt(`Unexpected error: ${r}`)});
|
|
1033
1039
|
//# sourceMappingURL=index.js.map
|