@teispace/next-maker 1.15.1 → 1.16.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 +12 -0
- package/README.md +418 -281
- package/dist/index.js +372 -136
- package/dist/index.js.map +4 -4
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.d.ts.map +1 -0
- package/dist/src/commands/env.d.ts +3 -0
- package/dist/src/commands/env.d.ts.map +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/layout.d.ts +3 -0
- package/dist/src/commands/layout.d.ts.map +1 -0
- package/dist/src/commands/provider.d.ts +3 -0
- package/dist/src/commands/provider.d.ts.map +1 -0
- package/dist/src/commands/remove.d.ts +3 -0
- package/dist/src/commands/remove.d.ts.map +1 -0
- package/dist/src/commands/setup.d.ts.map +1 -1
- package/dist/src/generators/index.d.ts +4 -0
- package/dist/src/generators/index.d.ts.map +1 -1
- package/dist/src/generators/layout.generator.d.ts +22 -0
- package/dist/src/generators/layout.generator.d.ts.map +1 -0
- package/dist/src/generators/provider.generator.d.ts +14 -0
- package/dist/src/generators/provider.generator.d.ts.map +1 -0
- package/dist/src/generators/templates/layout.template.d.ts +13 -0
- package/dist/src/generators/templates/layout.template.d.ts.map +1 -0
- package/dist/src/generators/templates/provider.template.d.ts +12 -0
- package/dist/src/generators/templates/provider.template.d.ts.map +1 -0
- package/dist/src/manifests/bundle-analyzer.manifest.d.ts +3 -0
- package/dist/src/manifests/bundle-analyzer.manifest.d.ts.map +1 -0
- package/dist/src/manifests/commitizen.manifest.d.ts +3 -0
- package/dist/src/manifests/commitizen.manifest.d.ts.map +1 -0
- package/dist/src/manifests/dark-theme.manifest.d.ts +3 -0
- package/dist/src/manifests/dark-theme.manifest.d.ts.map +1 -0
- package/dist/src/manifests/http-client.manifest.d.ts +3 -0
- package/dist/src/manifests/http-client.manifest.d.ts.map +1 -0
- package/dist/src/manifests/i18n.manifest.d.ts +3 -0
- package/dist/src/manifests/i18n.manifest.d.ts.map +1 -0
- package/dist/src/manifests/index.d.ts +23 -0
- package/dist/src/manifests/index.d.ts.map +1 -0
- package/dist/src/manifests/react-compiler.manifest.d.ts +3 -0
- package/dist/src/manifests/react-compiler.manifest.d.ts.map +1 -0
- package/dist/src/manifests/redux.manifest.d.ts +9 -0
- package/dist/src/manifests/redux.manifest.d.ts.map +1 -0
- package/dist/src/manifests/runner.d.ts +37 -0
- package/dist/src/manifests/runner.d.ts.map +1 -0
- package/dist/src/manifests/security-headers.manifest.d.ts +3 -0
- package/dist/src/manifests/security-headers.manifest.d.ts.map +1 -0
- package/dist/src/manifests/tests.manifest.d.ts +3 -0
- package/dist/src/manifests/tests.manifest.d.ts.map +1 -0
- package/dist/src/manifests/types.d.ts +105 -0
- package/dist/src/manifests/types.d.ts.map +1 -0
- package/dist/src/manifests/validate-scripts.manifest.d.ts +3 -0
- package/dist/src/manifests/validate-scripts.manifest.d.ts.map +1 -0
- package/dist/src/modifiers/env-var.modifier.d.ts +60 -0
- package/dist/src/modifiers/env-var.modifier.d.ts.map +1 -0
- package/dist/src/modifiers/index.d.ts +2 -0
- package/dist/src/modifiers/index.d.ts.map +1 -1
- package/dist/src/modifiers/root-provider.modifier.d.ts +26 -0
- package/dist/src/modifiers/root-provider.modifier.d.ts.map +1 -0
- package/dist/src/services/setup/commitizen/index.d.ts +3 -0
- package/dist/src/services/setup/commitizen/index.d.ts.map +1 -0
- package/dist/src/services/setup/commitizen/package-modifier.d.ts +17 -0
- package/dist/src/services/setup/commitizen/package-modifier.d.ts.map +1 -0
- package/dist/src/services/setup/security-headers/headers.d.ts +29 -0
- package/dist/src/services/setup/security-headers/headers.d.ts.map +1 -0
- package/dist/src/services/setup/security-headers/index.d.ts +3 -0
- package/dist/src/services/setup/security-headers/index.d.ts.map +1 -0
- package/dist/src/services/setup/validate-scripts/index.d.ts +4 -0
- package/dist/src/services/setup/validate-scripts/index.d.ts.map +1 -0
- package/dist/src/services/setup/validate-scripts/package-modifier.d.ts +19 -0
- package/dist/src/services/setup/validate-scripts/package-modifier.d.ts.map +1 -0
- package/dist/src/services/setup/validate-scripts/scripts.d.ts +12 -0
- package/dist/src/services/setup/validate-scripts/scripts.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
2
|
+
var an=Object.defineProperty;var Pe=(e,r)=>()=>(e&&(r=e(e=0)),r);var Pt=(e,r)=>{for(var t in r)an(e,t,{get:r[t],enumerable:!0})};import{existsSync as vt}from"node:fs";import de from"node:fs/promises";import Sr from"node:path";var d,f,_,T,S,k,m,P=Pe(()=>{"use strict";d=async e=>de.readFile(e,"utf-8"),f=async(e,r)=>{await de.mkdir(Sr.dirname(e),{recursive:!0}),await de.writeFile(e,r,"utf-8")},_=async(e,r)=>{await de.mkdir(Sr.dirname(r),{recursive:!0}),await de.copyFile(e,r)},T=async e=>{vt(e)&&await de.unlink(e)},S=async e=>{vt(e)&&await de.rm(e,{recursive:!0,force:!0})},k=async(e,r)=>{let t=await d(e),o=JSON.parse(t),n=r(o);await f(e,JSON.stringify(n,null,2))},m=e=>vt(e)});var Rr={};Pt(Rr,{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 Ir={};Pt(Ir,{cleanupHttpTypes:()=>ve,isFileUsed:()=>_n,isStringUsed:()=>Fn,migrateClientUsages:()=>bt,removeHttpExports:()=>Nt,updateConfigIndex:()=>$t,updateHttpIndex:()=>Tt,updateTypesIndex:()=>Rt,updateUtilsIndex:()=>Et});import{exec as kn}from"node:child_process";import F from"node:path";import{promisify as On}from"node:util";var Ct,Tt,Et,Rt,$t,ve,bt,Nt,_n,Fn,ot=Pe(()=>{"use strict";w();P();Ct=On(kn),Tt=async(e,r)=>{let t=F.join(e,c.HTTP_UTILS,"index.ts");if(m(t)){let o=await d(t);o=o.replace(/export .* from '\.\/axios-client';?\n?/g,""),o=o.replace(/export .* from '\.\/fetch-client';?\n?/g,""),r.includes("fetch")&&!o.includes("fetch-client")&&(o+=`export * from './fetch-client';
|
|
3
3
|
`),r.includes("axios")&&!o.includes("axios-client")&&(o+=`export * from './axios-client';
|
|
4
|
-
`),await
|
|
5
|
-
`,await
|
|
4
|
+
`),await f(t,o)}},Et=async e=>{let r=F.join(e,c.UTILS_INDEX);if(m(r)){let t=await d(r);t.includes("./http")||(t+=`export * from './http';
|
|
5
|
+
`,await f(r,t))}},Rt=async e=>{let r=F.join(e,c.TYPES_INDEX);if(m(r)){let t=await d(r);t.includes("./common")||(t+=`export * from './common';
|
|
6
6
|
`),t.includes("./utility")||(t+=`export * from './utility';
|
|
7
|
-
`),await
|
|
8
|
-
`,await
|
|
7
|
+
`),await f(r,t)}},$t=async e=>{let r=F.join(e,c.CONFIG_INDEX);if(m(r)){let t=await d(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 o=await d(t);r.includes("axios")||(o=o.replace(/declare module 'axios'\s*\{\s*export interface AxiosRequestConfig\s*\{[\s\S]*?\}\s*\}/g,""),o=`${o.replace(/\n\s*\n/g,`
|
|
9
9
|
|
|
10
10
|
`).trim()}
|
|
11
|
-
`,await
|
|
12
|
-
`).filter(
|
|
13
|
-
`).filter(
|
|
14
|
-
`).filter(f=>{let[g]=f.split(":"),y=_.resolve(e,g);return!t.some(x=>!!(y===x||y.startsWith(x+_.sep)))}).length>0:!1}catch(s){return console.warn(`Warning: Failed to check usage for string ${r}:`,s),!0}}});import{existsSync as Vi}from"node:fs";import{access as Ki}from"node:fs/promises";import Pr from"node:path";var oe,De,Tr=ue(()=>{"use strict";oe=async(e,r,t)=>{try{return await Ki(Pr.join(e,t,r)),!0}catch{return!1}},De=(e,r,t)=>Vi(Pr.join(e,r,t))});import{access as st,readFile as Wi}from"node:fs/promises";import Le from"node:path";var O,Sr=ue(()=>{"use strict";O=async e=>{try{let r=Le.join(e,"package.json"),t=await Wi(r,"utf-8"),o=JSON.parse(t),n={...o.dependencies,...o.devDependencies},i=!!(n["@reduxjs/toolkit"]&&n["react-redux"]),s=!!n["next-intl"],c=!!n.axios,p=!1,f=Le.join(e,"src","lib","utils","http","axios-client");try{await st(f)}catch{c=!1}let g=Le.join(e,"src","lib","utils","http","fetch-client");try{await st(g),p=!0}catch{p=!1}let y;c&&p?y="both":c?y="axios":p?y="fetch":y="none";let x=!!n.vitest;if(!x)try{await st(Le.join(e,"vitest.config.ts")),x=!0}catch{}return{hasRedux:i,httpClient:y,hasI18n:s,hasTests:x}}catch(r){throw new Error(`Failed to detect project setup: ${r}`,{cause:r})}}});var wr={};Ye(wr,{detectProjectSetup:()=>O,directoryExists:()=>oe,fileExistsAt:()=>De});var U=ue(()=>{"use strict";Tr();Sr()});import{Command as _s}from"commander";import nt from"node:path";import re from"picocolors";function Mt(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 Y from"picocolors";var Vo={reset:e=>e,red:Y.red,green:Y.green,yellow:Y.yellow,blue:Y.blue,cyan:Y.cyan,magenta:Y.magenta,white:Y.white,gray:Y.gray,bright:Y.bold,dim:Y.dim};function ae(e,r="reset"){let t=Vo[r]??(o=>o);console.log(t(e))}function _e(e){ae(`\u2716 ${e}`,"red")}var w=_e;function m(e){ae(e)}function Jt(){console.log(""),ae("\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"),ae("\u2551 \u2551","cyan"),ae("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),ae("\u2551 \u2551","cyan"),ae("\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 Ko from"ora";function I(e="",r){let t=Ko({text:e,...r});return t.start(),t}var Wo=e=>e.charAt(0).toUpperCase()+e.slice(1),$=e=>e.replace(/-([a-z])/g,(r,t)=>t.toUpperCase()),R=e=>Wo($(e));import qo from"ora";var h=qo();T();import{exec as Zo}from"node:child_process";import{promisify as Qo}from"node:util";var W=Qo(Zo),Bt=async(e,r,t)=>{try{if(await W("git init",{cwd:e}),await W("git add .",{cwd:e}),await W('git commit -m "Initial commit from @teispace/next-maker"',{cwd:e}),r&&(await W(`git remote add origin ${r}`,{cwd:e}),t)){await W("git fetch origin",{cwd:e});let o="main";try{await W("git show-branch origin/main",{cwd:e})}catch{try{await W("git show-branch origin/master",{cwd:e}),o="master"}catch{o="main"}}await W(`git merge origin/${o} --allow-unrelated-histories -X ours`,{cwd:e}),await W(`git push origin HEAD:${o}`,{cwd:e})}}catch(o){console.warn("Failed to initialize git repository",o)}};import{exec as ei}from"node:child_process";import{promisify as ti}from"node:util";var he=ti(ei),Xt=async(e,r)=>{let t=`${r} install`;try{await he(t,{cwd:e})}catch(o){throw new Error(`Failed to install dependencies with ${r}: ${o}`,{cause:o})}},G=async(e,r,t)=>{let o=r==="npm"?`${r} run ${t}`:`${r} ${t}`;try{await he(o,{cwd:e})}catch(n){console.warn(`Warning: Failed to run script '${t}': ${n}`)}},ri=()=>{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"},D=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":ri()},oi=async(e,r,t)=>{if(t.length===0)return;let n=`${Yt(r)} ${t.join(" ")}`;try{await he(n,{cwd:e})}catch(i){throw new Error(`Failed to install packages with ${r}: ${i}`,{cause:i})}},Yt=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"}},H=async(e,r)=>{let t=await D(e);await oi(e,t,[r])},ii=e=>{switch(e){case"npm":return"--save-dev";case"yarn":case"pnpm":case"bun":return"-D";default:return"--save-dev"}},Ke=async(e,r,t)=>{if(t.length===0)return;let o=Yt(r),n=ii(r),i=`${o} ${n} ${t.join(" ")}`;try{await he(i,{cwd:e})}catch(s){throw new Error(`Failed to install dev packages with ${r}: ${s}`,{cause:s})}},ke=async(e,r)=>{let t=await D(e);await Ke(e,t,[r])},ni=async(e,r,t)=>{if(t.length===0)return;let n=`${si(r)} ${t.join(" ")}`;try{await he(n,{cwd:e})}catch(i){throw new Error(`Failed to uninstall packages with ${r}: ${i}`,{cause:i})}},si=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"}},We=async(e,r)=>{let t=await D(e);await ni(e,t,[r])};import ai from"enquirer";var{prompt:ci}=ai,Vt=async e=>{let r=await ci([{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};T();import pi from"node:path";var ee=async(e,r)=>{let t=li(r);await u(pi.join(e,"test/test-utils.tsx"),t)},li=({redux:e,i18n:r})=>e&&r?mi:e?di:r?ui:fi,mi=`import { type RenderOptions, render } from '@testing-library/react';
|
|
11
|
+
`,await f(t,o))}},bt=async(e,r,t)=>{if(r===t)return;let o=r==="fetch"?"fetchClient":"axiosClient",n=t==="fetch"?"fetchClient":"axiosClient",i=r==="fetch"?"fetch-client":"axios-client",s=t==="fetch"?"fetch-client":"axios-client",a=F.join(e,"src");try{let p=`grep -rl "${o}" "${a}" --include="*.ts" --include="*.tsx" || true`,{stdout:u}=await Ct(p);if(!u.trim())return;let g=u.trim().split(`
|
|
12
|
+
`).filter(x=>x);for(let x of g){if(!m(x))continue;let y=await d(x),Y=!1,yt=new RegExp(`(import\\s*\\{[^}]*?)\\b${o}\\b([^}]*?\\}\\s*from\\s*['"][^'"]*?/http['"])`,"g");yt.test(y)&&(y=y.replace(yt,`$1${n}$2`),Y=!0);let Qe=new RegExp(`import\\s+${o}\\s+from\\s+['"]([^'"]*?)/${i}['"]`,"g");Qe.test(y)&&(y=y.replace(Qe,`import ${n} from '$1/${s}'`),Y=!0);let et=new RegExp(`\\b${o}\\b(?=\\.)`,"g");et.test(y)&&(y=y.replace(et,n),Y=!0);let yr=new RegExp(`\\b${o}\\b(?![.\\w])`,"g");yr.test(y)&&(y=y.replace(yr,n),Y=!0),Y&&await f(x,y)}}catch(p){console.warn("Warning: Could not scan for HTTP client usages:",p)}},Nt=async e=>{let r=F.join(e,c.UTILS_INDEX);if(m(r)){let o=await d(r);o=o.replace(/^\s*export\s+\*\s+from\s+['"]\.\/http['"];?.*$/gm,""),await f(r,o)}let t=F.join(e,c.TYPES_INDEX);if(m(t)){let o=await d(t),n=F.join(e,c.COMMON_TYPES_DIR);if(!m(n))o=o.replace(/^\s*export\s+\*\s+from\s+['"]\.\/common['"];?.*$/gm,"");else{let i=F.join(n,"index.ts");if(m(i)){let s=await d(i);s=s.replace(/^\s*export\s+\*\s+from\s+['"]\.\/http\.types['"];?.*$/gm,""),await f(i,s)}}m(F.join(e,c.UTILITY_TYPES_DIR))||(o=o.replace(/^\s*export\s+\*\s+from\s+['"]\.\/utility['"];?.*$/gm,"")),await f(t,o)}},_n=async(e,r,t=[])=>{let o=F.join(e,"src"),i=`grep -r "from ['\\"].*${r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}.*['\\"]" "${o}" --include="*.ts" --include="*.tsx" || true`;try{let{stdout:s}=await Ct(i);return s.trim()?s.trim().split(`
|
|
13
|
+
`).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 ${r}:`,s),!0}},Fn=async(e,r,t=[])=>{let o=F.join(e,"src"),i=`grep -r "${r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}" "${o}" --include="*.ts" --include="*.tsx" || true`;try{let{stdout:s}=await Ct(i);return s.trim()?s.trim().split(`
|
|
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';
|
|
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
|
+
`,En=`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
|
+
`,Rn=`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
|
+
`,$n=`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
|
|
178
|
-
`};n("CONTAINER_NAME",r.containerName||"next-app"),n("IMAGE_NAME",r.imageName||"nextjs-starter"),n("IMAGE_TAG",r.imageTag||"latest"),await
|
|
176
|
+
`;import $r 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 br=async(e,r)=>{if(r.bundleAnalyzer)return;let t=$r.join(e,c.NEXT_CONFIG);if(m(t)){let o=await d(t);o=o.replace(/import\s+withBundleAnalyzer\s+from\s+'@next\/bundle-analyzer';\n/,""),o=o.replace(/const\s+bundleAnalyzer\s*=\s*withBundleAnalyzer\(\{[\s\S]*?\}\);\n+/,""),o=o.replace(/bundleAnalyzer\(([^)]*)\)/,"$1"),await f(t,o)}await k($r.join(e,c.PACKAGE_JSON),o=>(o.devDependencies&&delete o.devDependencies[C.NEXT_BUNDLE_ANALYZER],o.scripts&&delete o.scripts.analyze,o))};w();P();import Re from"node:path";var Nr=async(e,r)=>{r.darkMode||(await T(Re.join(e,c.THEME_PROVIDER)),await bn(e),await Nn(e),await An(e))},bn=async e=>{let r=Re.join(e,c.PROVIDERS_INDEX),t=await d(r);t=t.replace(/export \* from '\.\/CustomThemeProvider';\n/,""),await f(r,t)},Nn=async e=>{let r=Re.join(e,c.GLOBALS_CSS);if(m(r)){let t=await d(r);t=t.replace(/@custom-variant dark \(.*?\);\n\n/,""),t=t.replace(/@theme \{[\s\S]*?\}\n/,""),await f(r,t)}},In=e=>e.replace(/\bbg-light\b\s?/g,"").replace(/\bdark:bg-dark\b\s?/g,"").replace(/\s{2,}/g," ").replace(/suppressHydrationWarning=\{true\}/g,""),An=async e=>{let r=[Re.join(e,c.ROOT_LAYOUT),Re.join(e,"src/app/[locale]/layout.tsx")];for(let t of r)if(m(t)){let o=await d(t),n=In(o);n!==o&&await f(t,n)}};w();P();ot();import b from"node:path";var Ar=async(e,r)=>{let t=b.join(e,c.HTTP_UTILS),o=r.httpClient!=="none"||!!r.reactSecureStorage;r.httpClient==="none"?await jn(e,t,o):r.httpClient==="axios"?await Dn(e,t):r.httpClient==="fetch"&&await Ln(e,t)},jn=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 Mn(e),await T(b.join(e,c.APP_APIS)),await Gn(e)),await Hn(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 Un(e))},Dn=async(e,r)=>{await S(b.join(e,c.FETCH_CLIENT));let t=await d(b.join(r,"index.ts"));t=t.replace(/export .* from '\.\/fetch-client';\n/g,""),await f(b.join(r,"index.ts"),t);let o=b.join(e,c.HTTP_TYPES);if(m(o)){let n=await d(o);n=n.replace(/export interface FetchClientOptions \{[\s\S]*?\}\n\n/,""),n=n.replace(/export interface ExtendedRequestInit extends RequestInit \{[\s\S]*?\}\n\n/,""),await f(o,n)}},Ln=async(e,r)=>{await S(b.join(e,c.AXIOS_CLIENT));let t=await d(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 o=b.join(e,c.HTTP_TYPES);if(m(o)){let n=await d(o);n=n.replace(/export interface AxiosClientOptions \{[\s\S]*?\}\n\n/,""),await f(o,n)}},Mn=async e=>{let r=b.join(e,c.UTILS_INDEX);if(m(r)){let t=await d(r);t=t.replace(/export \* from '\.\/http';\n/,""),await f(r,t)}},Gn=async e=>{let r=b.join(e,c.CONFIG_INDEX);if(m(r)){let t=await d(r);t=t.replace(/export \* from '\.\/app-apis';\n/,""),await f(r,t)}},Hn=async e=>{let r=b.join(e,c.CONSTANTS);if(m(r)){let t=await d(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)}},Un=async e=>{let r=b.join(e,c.TYPES_INDEX);if(m(r)){let t=await d(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 kr=async(e,r)=>{r.i18n||(await Vn(e),await Jn(e),await Bn(e),await Xn(e),await zn(e))},zn=async e=>{let r=H.join(e,c.ENV_EXAMPLE);if(!m(r))return;let t=await d(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)},Vn=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))},Jn=async e=>{let r=H.join(e,c.TYPES_INDEX);if(m(r)){let o=await d(r);o=o.replace(/export \* from '\.\/i18n';\n/,""),await f(r,o)}let t=H.join(e,c.CONFIG_INDEX);if(m(t)){let o=await d(t);o=o.replace(/export \* from '\.\/app-locales';\n/,""),await f(t,o)}},Bn=async e=>{let r=H.join(e,c.NEXT_CONFIG);if(m(r)){let t=await d(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)}},Xn=async e=>{let r=H.join(e,c.COUNTER_COMPONENT);if(m(r)){let n=await d(r);n=n.replace(/import\s+\{\s*useTranslations\s*\}\s+from\s+['"]next-intl['"];\n/,""),n=n.replace(/\s*const\s+t\s*=\s*useTranslations\(['"]Count['"]\);\n/,""),n=n.replace(/\{t\('currentCount',\s*\{\s*count:\s*value\s*\}\)\}/g,"Current Count: {value}"),n=n.replace(/\{t\(['"]increment['"]\)\}/g,"Increment"),n=n.replace(/\{t\(['"]decrement['"]\)\}/g,"Decrement"),n=n.replace(/\{t\(['"]reset['"]\)\}/g,"Reset"),await f(r,n)}let t=H.join(e,"src/features/counter/components/Counter.test.tsx"),o=H.join(e,"test/test-utils.tsx");await T(t),await T(o)};w();P();import oe from"node:path";var Or=async(e,r)=>{r.httpClient!=="none"||r.reactSecureStorage||await S(oe.join(e,c.STORAGE_SERVICE))},_r=async e=>{await T(oe.join(e,c.LICENSE))},Fr=async e=>{await T(oe.join(e,c.CHANGELOG))},jr=async e=>{await T(oe.join(e,c.NVM_RC)),await T(oe.join(e,c.NPM_RC))},Dr=async e=>{await S(oe.join(e,c.CLAUDE_DIR)),await S(oe.join(e,c.DOCS_DIR)),await S(oe.join(e,c.VSCODE_DIR))};import Lr from"node:path";w();P();var Mr=async(e,r)=>{if(r.reactCompiler)return;let t=Lr.join(e,c.NEXT_CONFIG);if(m(t)){let o=await d(t);o=o.replace(/^\s*reactCompiler:\s*true,?\n/m,""),await f(t,o)}await k(Lr.join(e,c.PACKAGE_JSON),o=>(o.devDependencies&&delete o.devDependencies[C.REACT_COMPILER_BABEL],o))};w();P();import fe from"node:path";var Gr=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 Yn(e),await Kn(e))},Yn=async e=>{let r=fe.join(e,c.PROVIDERS_INDEX),t=await d(r);t=t.replace(/export \* from '\.\/StoreProvider';\n/,""),await f(r,t)},Kn=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 o=await d(t);o=o.replace(/import\s+\{\s*Counter\s*\}\s+from\s+['"]@\/features\/counter['"];\n?/,""),o=o.replace(/<Counter\s*\/>\n?/g,""),await f(t,o)}};import it from"node:path";w();P();var qn=[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],Hr=async(e,r)=>{r.tests||(await T(it.join(e,c.VITEST_CONFIG)),await S(it.join(e,c.TEST_DIR)),await T(it.join(e,"src/features/counter/components/Counter.test.tsx")),await k(it.join(e,c.PACKAGE_JSON),t=>{if(t.devDependencies)for(let o of qn)delete t.devDependencies[o];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 Ur=async(e,r)=>{let t=R("Customizing features...");try{await Ar(e,r),await Or(e,r),await Gr(e,r),await Nr(e,r),await br(e,r),await kr(e,r),await Mr(e,r),await Hr(e,r),await _r(e),await Fr(e),await jr(e),await Dr(e),r.tests&&await re(e,{redux:r.redux,i18n:r.i18n}),t.succeed("Features customized.")}catch(o){throw t.fail("Failed to customize features."),o}};import Wn from"node:path";w();P();var zr=async(e,r)=>{let t=R("Configuring package.json...");try{let o=r.gitRemote,n=r.gitHomepage,i=r.gitIssues;r.gitRemote&&(r.gitRemote.startsWith("git@github.com:")&&(o=r.gitRemote.replace("git@github.com:","https://github.com/").replace(/\.git$/,"")),n||(n=`${o.replace(/\.git$/,"")}#readme`),i||(i=`${o.replace(/\.git$/,"")}/issues`),o.endsWith(".git")||(o=`${o}.git`)),await k(Wn.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?(n&&(s.homepage=n),i&&(s.bugs={url:i}),o&&(s.repository={type:"git",url:o})):(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(o){throw t.fail("Failed to configure package.json."),o}};import{readdir as Vr}from"node:fs/promises";import N from"node:path";w();P();var Jr=async(e,r)=>{await Zn(e,r),await Qn(e,r),await es(e,r),await ts(e,r),await rs(e,r),await os(e,r),await is(e,r)},Zn=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)))},Qn=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)))},es=async(e,r)=>{r.ci||await S(N.join(e,c.GITHUB_WORKFLOWS))},ts=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 o=N.join(t,c.GITHUB_ISSUE_TEMPLATE),n=N.join(t,c.GITHUB_PR_TEMPLATE),i=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(n)){let s=await d(n);s=i(s),await f(n,s)}try{if(m(o)){let s=await Vr(o);for(let a of s){let p=N.join(o,a),u=await d(p);u=i(u),await f(p,u)}}}catch{}}},rs=async(e,r)=>{let t=[c.CODE_OF_CONDUCT,c.CONTRIBUTING,c.SECURITY];for(let o of t)r.communityFiles.includes(o)||await T(N.join(e,o))},os=async(e,r)=>{if(r.docker){let t=N.join(e,c.ENV_EXAMPLE);if(m(t)){let o=await d(t),n=(i,s)=>{let a=new RegExp(`${i}=.*`);a.test(o)?o=o.replace(a,`${i}=${s}`):o+=`${i}=${s}
|
|
178
|
+
`};n("CONTAINER_NAME",r.containerName||"next-app"),n("IMAGE_NAME",r.imageName||"nextjs-starter"),n("IMAGE_TAG",r.imageTag||"latest"),await f(t,o)}}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 o=await d(t);o=o.replace(/# Docker Compose Configuration\n/,""),o=o.replace(/CONTAINER_NAME=.*\n/,""),o=o.replace(/IMAGE_NAME=.*\n/,""),o=o.replace(/IMAGE_TAG=.*\n/,""),await f(t,o)}}},is=async(e,r)=>{let t=async i=>{let s=await Vr(i,{withFileTypes:!0}),a=[];for(let p of s){let u=N.join(i,p.name);p.isDirectory()&&p.name!=="node_modules"&&p.name!==".git"?a.push(...await t(u)):p.isFile()&&p.name.toLowerCase()==="readme.md"&&a.push(u)}return a},o=await t(e),n=N.join(e,c.README);for(let i of o)i!==n&&await T(i);if(r.readme){let i=`# ${r.projectName}
|
|
179
179
|
|
|
180
180
|
${r.description}
|
|
181
181
|
|
|
@@ -188,7 +188,7 @@ ${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
|
|
191
|
+
`;await f(n,i)}else await T(n)};w();P();import It from"node:path";var Br=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 o=["children: React.ReactNode;"],n=["children"];r.i18n&&(o.push("locale: SupportedLocale;"),o.push("messages: AbstractIntlMessages;"),o.push("timeZone: string;"),n.push("locale","messages","timeZone")),r.redux&&(o.push("preloadedState?: Partial<AppState>;"),n.push("preloadedState"));let i="{children}";r.i18n&&(i=`<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
|
|
192
192
|
${i}
|
|
193
193
|
</NextIntlClientProvider>`),r.darkMode&&(i=`<CustomThemeProvider>
|
|
194
194
|
${i}
|
|
@@ -197,7 +197,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
|
|
|
197
197
|
</StoreProvider>`),i==="{children}"&&(i="<>{children}</>");let s=`type RootProviderProps = {
|
|
198
198
|
${o.join(`
|
|
199
199
|
`)}
|
|
200
|
-
};`,
|
|
200
|
+
};`,a=`'use client';
|
|
201
201
|
${t.join(`
|
|
202
202
|
`)}
|
|
203
203
|
|
|
@@ -208,7 +208,7 @@ export const RootProvider = ({ ${n.join(", ")} }: RootProviderProps) => {
|
|
|
208
208
|
${i}
|
|
209
209
|
);
|
|
210
210
|
};
|
|
211
|
-
`;await
|
|
211
|
+
`;await f(It.join(e,c.ROOT_PROVIDER),a)},Xr=async(e,r)=>{if(r.i18n)return;let t=`\`\${livvic.variable} ${r.darkMode?"bg-light antialiased dark:bg-dark":"antialiased"}\``,o=(r.redux,""),n=`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';
|
|
@@ -240,10 +240,10 @@ export default function RootLayout({
|
|
|
240
240
|
</html>
|
|
241
241
|
);
|
|
242
242
|
}
|
|
243
|
-
`;await
|
|
243
|
+
`;await f(It.join(e,c.ROOT_LAYOUT),n);let i=r.redux?`import { Counter } from '@/features/counter';
|
|
244
244
|
|
|
245
245
|
`:"",s=r.redux?`
|
|
246
|
-
<Counter />`:"",
|
|
246
|
+
<Counter />`:"",a=`${i}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,13 +251,13 @@ export default function RootLayout({
|
|
|
251
251
|
</div>
|
|
252
252
|
);
|
|
253
253
|
}
|
|
254
|
-
`;await
|
|
255
|
-
Cleaning up: Deleting directory ${r.projectName}...`));try{await
|
|
256
|
-
Process interrupted. Cleaning up...`)),await n(),process.exit(1)};process.on("SIGINT",i),process.on("SIGTERM",i);try{if(await
|
|
257
|
-
`)},
|
|
258
|
-
`;var
|
|
259
|
-
`);let
|
|
260
|
-
`;r.includes(t.trim())||(r+=t,await
|
|
254
|
+
`;await f(It.join(e,c.ROOT_PAGE),a)};import ns from"degit";var Yr=async e=>{let r=R("Downloading template...");try{await ns("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 Kr=e=>{e.command("init").description("Initialize a new Next.js project").argument("[name]","Project name").action(async r=>{await ss(r)})},ss=async e=>{vr(),l("Welcome to the Teispace Next.js App Creator!"),l("");let r=await Er(e),t=At.resolve(process.cwd(),r.projectName);m(t)&&(console.error(ie.red(`Error: Directory ${r.projectName} already exists.`)),process.exit(1));let o=R("Initializing project..."),n=async()=>{if(m(t)){o.stop(),console.log(ie.yellow(`
|
|
255
|
+
Cleaning up: Deleting directory ${r.projectName}...`));try{await S(t),console.log(ie.green("Cleanup successful."))}catch(s){console.error(ie.red(`Failed to clean up directory ${r.projectName}:`),s)}}},i=async()=>{console.log(ie.red(`
|
|
256
|
+
Process interrupted. Cleaning up...`)),await n(),process.exit(1)};process.on("SIGINT",i),process.on("SIGTERM",i);try{if(await Yr(t),await zr(t,r),await Ur(t,r),o.text="Generating code...",await Br(t,r),await Xr(t,r),o.text="Setting up developer tools...",await Jr(t,r),o.text="Initializing Git...",await wr(t,r.gitRemote,r.pushToRemote),o.text="Installing dependencies...",await Cr(t,r.packageManager),o.text="Formatting and Linting...",await U(t,r.packageManager,"lint:fix"),r.copyEnv){o.text="Creating .env file...";let s=At.join(t,".env.example"),a=At.join(t,".env");m(s)&&await(await import("node:fs/promises")).copyFile(s,a)}process.off("SIGINT",i),process.off("SIGTERM",i),o.succeed(ie.green(`Project ${r.projectName} created successfully!`)),l(""),l("To get started:"),l(ie.cyan(` cd ${r.projectName}`)),l(ie.cyan(` ${r.packageManager==="npm"?"npm run dev":`${r.packageManager} dev`}`)),l("")}catch(s){o.fail("Failed to create project."),console.error(s),await n(),process.exit(1)}};import Bt from"node:path";import q from"picocolors";L();import{mkdir as eo,writeFile as _t}from"node:fs/promises";import ge from"node:path";P();var Ot=e=>{let{componentName:r,isClient:t,hasI18n:o}=e,n=e.translationNamespace||r,i=[];return(t||o)&&i.push("'use client';"),o&&i.push("import { useTranslations } from 'next-intl';"),i.push(""),i.push(`export function ${r}() {`),o?(i.push(` const t = useTranslations('${n}');`),i.push(""),i.push(" return ("),i.push(" <div>"),i.push(" <h2>{t('title')}</h2>"),i.push(" </div>"),i.push(" );")):(i.push(" return ("),i.push(" <div>"),i.push(` <h2>${r}</h2>`),i.push(" </div>"),i.push(" );")),i.push("}"),i.push(""),i.join(`
|
|
257
|
+
`)},Qr=e=>`export { ${e.componentName} } from './${e.componentName}';
|
|
258
|
+
`;var Ft=async e=>{let{name:r,projectPath:t,isClient:o,hasI18n:n,featurePath:i}=e,s=$(r);i?await ls(s,t,i,o,n):await ms(s,r,t,o,n)},ls=async(e,r,t,o,n)=>{let i=ge.join(r,t,"components");await eo(i,{recursive:!0}),await _t(ge.join(i,`${e}.tsx`),Ot({componentName:e,isClient:o,hasI18n:n}))},ms=async(e,r,t,o,n)=>{let i=ge.join(t,"src","components","common",e);await eo(i,{recursive:!0}),await _t(ge.join(i,`${e}.tsx`),Ot({componentName:e,isClient:o,hasI18n:n})),await _t(ge.join(i,"index.ts"),Qr({componentName:e}));let s=ge.join(t,"src","components","common","index.ts");await ds(s,`export { ${e} } from './${e}';
|
|
259
|
+
`);let a=ge.join(t,"src","components","index.ts");await us(a)},ds=async(e,r)=>{let t="";m(e)&&(t=await d(e)),t.includes(r.trim())||(t+=r,await f(e,t))},us=async e=>{let r="";m(e)&&(r=await d(e));let t=`export * from './common';
|
|
260
|
+
`;r.includes(t.trim())||(r+=t,await f(e,r))};import{writeFile as fs}from"node:fs/promises";import gs from"node:path";var to=e=>{let{camelName:r,pascalName:t,httpClient:o}=e,n=o==="axios"?"import { axiosClient } from '@/lib/utils/http';":"import { fetchClient } from '@/lib/utils/http';",i=o==="axios"?"axiosClient":"fetchClient";return`import { AppApis } from '@/lib/config';
|
|
261
261
|
${n}
|
|
262
262
|
import type { ResultAsync } from '@/types';
|
|
263
263
|
|
|
@@ -305,14 +305,14 @@ export const ${r}Service = {
|
|
|
305
305
|
return ${i}.delete<void>(AppApis.${r}.delete(id));
|
|
306
306
|
},
|
|
307
307
|
};
|
|
308
|
-
`},
|
|
308
|
+
`},ro=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 jt=async e=>{let{name:r,outputPath:t,httpClient:o}=e,n=A(r),i=$(r);await fs(gs.join(t,`${r}.service.ts`),to({camelName:n,pascalName:i,httpClient:o}))};import{mkdir as Ps,writeFile as ct}from"node:fs/promises";import V from"node:path";import{writeFile as hs}from"node:fs/promises";import xs from"node:path";var oo=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,16 +321,16 @@ export const ${r}Service = {
|
|
|
321
321
|
return ${t==="axios"?"axiosClient":"fetchClient"}.get<string>(AppApis.${r}.getAll);
|
|
322
322
|
},
|
|
323
323
|
};
|
|
324
|
-
`};var
|
|
324
|
+
`};var be=async e=>{let{name:r,outputPath:t,httpClient:o}=e,n=A(r);await hs(xs.join(t,`${r}.service.ts`),oo({camelName:n,httpClient:o}))};import{mkdir as ys,writeFile as Ne}from"node:fs/promises";import Ie from"node:path";var io=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
|
-
`},
|
|
328
|
+
`},no=e=>{let{featureName:r,componentName:t,hookName:o,withStore:n,withService:i}=e;return`export { default as ${t} } from './components/${t}';
|
|
329
329
|
export { ${o} } from './hooks/${o}';
|
|
330
330
|
export * from './types/${r}.types';${n?`
|
|
331
331
|
export * from './store';`:""}${i?`
|
|
332
332
|
export * from './services/${r}.service';`:""}
|
|
333
|
-
`};var
|
|
333
|
+
`};var so=e=>{let{componentName:r,camelName:t,typesImportPath:o}=e;return`import type { PersistConfig } from 'redux-persist';
|
|
334
334
|
import storage from 'redux-persist/lib/storage';
|
|
335
335
|
import type { ${r}State } from '${o}';
|
|
336
336
|
|
|
@@ -339,11 +339,11 @@ export const ${t}PersistConfig: PersistConfig<${r}State> = {
|
|
|
339
339
|
storage,
|
|
340
340
|
// whitelist: ['someField'], // Specify which fields to persist
|
|
341
341
|
};
|
|
342
|
-
`};var
|
|
342
|
+
`};var ao=e=>{let{componentName:r,camelName:t,sliceName:o}=e;return`import { RootState } from '@/store/rootReducer';
|
|
343
343
|
|
|
344
344
|
export const select${r}State = (state: RootState) => state.${t};
|
|
345
345
|
export { setLoading, setError, resetState } from './${o}.slice';
|
|
346
|
-
`};var
|
|
346
|
+
`};var co=e=>{let{componentName:r,camelName:t,typesImportPath:o}=e;return`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
|
347
347
|
import type { ${r}State } from '${o}';
|
|
348
348
|
|
|
349
349
|
const initialState: ${r}State = {
|
|
@@ -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 at=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 Ae=async e=>{let{name:r,outputPath:t,persist:o,typesImportPath:n=`./${r}.types`}=e;await ys(t,{recursive:!0});let i=$(r),s=A(r);await Promise.all([Ne(Ie.join(t,`${r}.types.ts`),at({componentName:i,withStore:!0})),Ne(Ie.join(t,`${r}.slice.ts`),co({componentName:i,camelName:s,typesImportPath:n})),Ne(Ie.join(t,`${r}.selectors.ts`),ao({componentName:i,camelName:s,sliceName:r})),...o?[Ne(Ie.join(t,"persist.ts"),so({componentName:i,camelName:s,typesImportPath:n}))]:[],Ne(Ie.join(t,"index.ts"),io({sliceName:r,withPersist:o}))])};var po=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,7 +393,7 @@ export function ${r}() {
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
export default ${r};
|
|
396
|
-
`};var
|
|
396
|
+
`};var lo=e=>{let{hookName:r,componentName:t,featureName:o}=e;return`'use client';
|
|
397
397
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
|
398
398
|
import { select${t}State, setLoading, setError } from '../store/${o}.selectors';
|
|
399
399
|
|
|
@@ -417,7 +417,7 @@ export const ${r} = () => {
|
|
|
417
417
|
setError: handleSetError,
|
|
418
418
|
} as const;
|
|
419
419
|
};
|
|
420
|
-
`},
|
|
420
|
+
`},mo=e=>{let{hookName:r}=e;return`'use client';
|
|
421
421
|
import { useState } from 'react';
|
|
422
422
|
|
|
423
423
|
export const ${r} = () => {
|
|
@@ -429,15 +429,36 @@ export const ${r} = () => {
|
|
|
429
429
|
// Add your methods here
|
|
430
430
|
} as const;
|
|
431
431
|
};
|
|
432
|
-
`};var
|
|
433
|
-
`)};var
|
|
434
|
-
|
|
432
|
+
`};var Dt=async e=>{let{name:r,outputPath:t,createStore:o,persistStore:n,createService:i,httpClient:s}=e,a=$(r),p=`use${a}`,u=[V.join(t,"components"),V.join(t,"hooks"),V.join(t,"types"),...o?[V.join(t,"store")]:[],...i?[V.join(t,"services")]:[]];await Promise.all(u.map(g=>Ps(g,{recursive:!0}))),await Promise.all([ct(V.join(t,"components",`${a}.tsx`),po({componentName:a,hookName:p})),ct(V.join(t,"hooks",`${p}.ts`),o?lo({hookName:p,componentName:a,featureName:r}):mo({hookName:p})),ct(V.join(t,"types",`${r}.types.ts`),at({componentName:a,withStore:o})),ct(V.join(t,"index.ts"),no({featureName:r,componentName:a,hookName:p,withStore:o,withService:i}))]),o&&await Ae({name:r,outputPath:V.join(t,"store"),persist:n,typesImportPath:`../types/${r}.types`}),i&&s&&await be({name:r,outputPath:V.join(t,"services"),httpClient:s})};import{mkdir as vs,writeFile as Ss}from"node:fs/promises";import Lt from"node:path";var uo=e=>{let{hookName:r,isClient:t}=e,o=[];return t&&o.push("'use client';"),o.push("import { useState, useCallback } from 'react';"),o.push(""),o.push(`export const ${r} = () => {`),o.push(" const [loading, setLoading] = useState(false);"),o.push(" const [error, setError] = useState<string | null>(null);"),o.push(""),o.push(" const execute = useCallback(async () => {"),o.push(" setLoading(true);"),o.push(" setError(null);"),o.push(" try {"),o.push(" // Add your logic here"),o.push(" } catch (err) {"),o.push(" setError(err instanceof Error ? err.message : 'An error occurred');"),o.push(" } finally {"),o.push(" setLoading(false);"),o.push(" }"),o.push(" }, []);"),o.push(""),o.push(" return {"),o.push(" loading,"),o.push(" error,"),o.push(" execute,"),o.push(" } as const;"),o.push("};"),o.push(""),o.join(`
|
|
433
|
+
`)};var Mt=async e=>{let{name:r,projectPath:t,isClient:o,featurePath:n}=e,s=`use${$(r)}`,a;n?a=Lt.join(t,n,"hooks"):a=Lt.join(t,"src","hooks"),await vs(a,{recursive:!0}),await Ss(Lt.join(a,`${s}.ts`),uo({hookName:s,isClient:o}))};import{mkdir as Ts}from"node:fs/promises";import pt from"node:path";P();var ws=e=>`import { setRequestLocale } from 'next-intl/server';
|
|
434
|
+
import type { SupportedLocale } from '@/types/i18n';
|
|
435
|
+
|
|
436
|
+
type Props = {
|
|
437
|
+
children: React.ReactNode;
|
|
438
|
+
params: Promise<{ locale: string }>;
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
export default async function ${e}({ children, params }: Props) {
|
|
442
|
+
const { locale } = await params;
|
|
443
|
+
setRequestLocale(locale as SupportedLocale);
|
|
444
|
+
|
|
445
|
+
return <>{children}</>;
|
|
446
|
+
}
|
|
447
|
+
`,Cs=e=>`type Props = {
|
|
448
|
+
children: React.ReactNode;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
export default function ${e}({ children }: Props) {
|
|
452
|
+
return <>{children}</>;
|
|
453
|
+
}
|
|
454
|
+
`,fo=({componentName:e,hasI18n:r})=>r?ws(e):Cs(e);var Es=/^[a-z][a-z0-9-]*$/,Rs=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,$s=e=>{if(!Es.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.`)},bs=e=>{if(!Rs.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},Gt=async e=>{let{segment:r,at:t,projectPath:o,hasI18n:n,withGroup:i}=e;$s(r),t&&bs(t);let s=`${$(r)}Layout`,a=i?`(${r})`:r,p=["src","app"];n&&p.push("[locale]"),t&&p.push(...t.split("/")),p.push(a);let u=pt.join(o,...p),g=pt.join(u,"layout.tsx");if(m(g))throw new Error(`Layout already exists at ${pt.relative(o,g)}.`);return await Ts(u,{recursive:!0}),await f(g,fo({componentName:s,hasI18n:n})),{layoutFile:g,displayPath:pt.relative(o,g),componentName:s}};P();import{copyFile as Ns,readFile as Is,writeFile as As}from"node:fs/promises";import ke from"node:path";var Ht=async e=>{let{code:r,name:t,country:o,flag:n,projectPath:i,copyTranslations:s}=e;await ks(i,r,s),await Os(i,r),await _s(i,r,t,o,n)},ks=async(e,r,t)=>{let o=ke.join(e,"src","i18n","translations"),n=ke.join(o,"en.json"),i=ke.join(o,`${r}.json`);if(t)await Ns(n,i);else{let s=await Is(n,"utf-8"),a=JSON.parse(s),p=go(a);await As(i,`${JSON.stringify(p,null,2)}
|
|
455
|
+
`)}},go=e=>{let r={};for(let[t,o]of Object.entries(e))typeof o=="object"&&o!==null?r[t]=go(o):r[t]="";return r},Os=async(e,r)=>{let t=ke.join(e,"src","types","i18n.ts");if(!m(t))return;let o=await d(t),n=/export type SupportedLocale = (.*);/,i=o.match(n);if(i&&!i[1].includes(`'${r}'`)){let s=`${i[1]} | '${r}'`;o=o.replace(n,`export type SupportedLocale = ${s};`),await f(t,o)}},_s=async(e,r,t,o,n)=>{let i=ke.join(e,"src","lib","config","app-locales.ts");if(!m(i))return;let s=await d(i);if(s.includes(`locale: '${r}'`))return;let a=s.lastIndexOf("];");if(a===-1)return;let p=` {
|
|
435
456
|
name: '${t}',
|
|
436
457
|
locale: '${r}',
|
|
437
458
|
flag: '${n}',
|
|
438
459
|
country: '${o}',
|
|
439
460
|
},
|
|
440
|
-
`;s=s.slice(0,
|
|
461
|
+
`;s=s.slice(0,a)+p+s.slice(a),await f(i,s)};import{mkdir as Fs,writeFile as Ut}from"node:fs/promises";import Oe from"node:path";var ho=e=>{let{componentName:r,routePath:t,hasI18n:o}=e;return o?`import { generateSEOMetadata } from '@/lib/config/seo';
|
|
441
462
|
import type { Metadata } from 'next';
|
|
442
463
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
443
464
|
import type { SupportedLocale } from '@/types/i18n';
|
|
@@ -483,7 +504,7 @@ export default function ${r}Page() {
|
|
|
483
504
|
</div>
|
|
484
505
|
);
|
|
485
506
|
}
|
|
486
|
-
`},
|
|
507
|
+
`},xo=e=>{let{componentName:r,paramName:t,routePath:o,hasI18n:n}=e;return n?`import { generateSEOMetadata } from '@/lib/config/seo';
|
|
487
508
|
import type { Metadata } from 'next';
|
|
488
509
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
489
510
|
import type { SupportedLocale } from '@/types/i18n';
|
|
@@ -537,14 +558,14 @@ export default async function ${r}Page(props: Props) {
|
|
|
537
558
|
</div>
|
|
538
559
|
);
|
|
539
560
|
}
|
|
540
|
-
`},
|
|
561
|
+
`},yo=e=>`export default function ${e.componentName}Loading() {
|
|
541
562
|
return (
|
|
542
563
|
<div className="flex h-full items-center justify-center">
|
|
543
564
|
<p>Loading...</p>
|
|
544
565
|
</div>
|
|
545
566
|
);
|
|
546
567
|
}
|
|
547
|
-
`,
|
|
568
|
+
`,Po=e=>`'use client';
|
|
548
569
|
|
|
549
570
|
export default function ${e.componentName}Error({
|
|
550
571
|
error,
|
|
@@ -561,7 +582,28 @@ export default function ${e.componentName}Error({
|
|
|
561
582
|
</div>
|
|
562
583
|
);
|
|
563
584
|
}
|
|
564
|
-
`;var
|
|
585
|
+
`;var zt=async e=>{let{name:r,projectPath:t,hasI18n:o,dynamic:n,withLoading:i=!1,withError:s=!1}=e,a=$(r),p=`/${r}`,u=o?"src/app/[locale]":"src/app",g;n?g=Oe.join(t,u,r,`[${n}]`):g=Oe.join(t,u,r),await Fs(g,{recursive:!0});let x=n?xo({componentName:a,routePath:n?`${p}/[${n}]`:p,paramName:n,hasI18n:o}):ho({componentName:a,routePath:p,hasI18n:o});await Ut(Oe.join(g,"page.tsx"),x),i&&await Ut(Oe.join(g,"loading.tsx"),yo({componentName:a})),s&&await Ut(Oe.join(g,"error.tsx"),Po({componentName:a}))};w();import{mkdir as js}from"node:fs/promises";import Vt from"node:path";P();var vo=(e,r)=>e.endsWith(r)?e:`${e}${r}`,So=({baseName:e})=>{let r=vo(e,"Provider"),t=r.replace(/Provider$/,""),o=`${t}ContextValue`,n=`${t}Context`,i=`use${t}`;return`'use client';
|
|
586
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
587
|
+
|
|
588
|
+
type ${o} = {
|
|
589
|
+
// TODO: declare context value fields
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const ${n} = createContext<${o} | null>(null);
|
|
593
|
+
|
|
594
|
+
export const ${i} = (): ${o} => {
|
|
595
|
+
const ctx = useContext(${n});
|
|
596
|
+
if (!ctx) {
|
|
597
|
+
throw new Error('${i} must be used within ${r}');
|
|
598
|
+
}
|
|
599
|
+
return ctx;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
export const ${r} = ({ children }: { children: React.ReactNode }) => {
|
|
603
|
+
const value = useMemo<${o}>(() => ({}), []);
|
|
604
|
+
return <${n}.Provider value={value}>{children}</${n}.Provider>;
|
|
605
|
+
};
|
|
606
|
+
`},wo=e=>vo(e,"Provider");var Ds=/^[A-Za-z][A-Za-z0-9-]*$/,Ls=e=>e.includes("-")?$(e):e.charAt(0).toUpperCase()+e.slice(1),Jt=async e=>{let{name:r,projectPath:t}=e;if(!Ds.test(r))throw new Error(`Invalid provider name "${r}". Use kebab-case or PascalCase letters and digits only.`);let o=Ls(r).replace(/Provider$/,""),n=wo(o),i=n,s=Vt.join(t,c.PROVIDERS),a=Vt.join(s,`${i}.tsx`);if(m(a))throw new Error(`Provider already exists at ${Vt.relative(t,a)}.`);return await js(s,{recursive:!0}),await f(a,So({baseName:o})),{componentName:n,fileBaseName:i,providerFile:a}};P();import _e from"node:path";var Co=e=>{let{componentName:r,sourceImportPath:t,testUtilsImportPath:o,hasRedux:n,hasI18n:i}=e,s=[];i&&s.push("messages: {}"),n&&s.push("preloadedState: {}");let a=s.length?`, {
|
|
565
607
|
${s.join(`,
|
|
566
608
|
`)},
|
|
567
609
|
}`:"";return`import { describe, expect, it } from 'vitest';
|
|
@@ -570,12 +612,12 @@ import { ${r} } from '${t}';
|
|
|
570
612
|
|
|
571
613
|
describe('${r}', () => {
|
|
572
614
|
it('renders without crashing', () => {
|
|
573
|
-
renderWithProviders(<${r} />${
|
|
615
|
+
renderWithProviders(<${r} />${a});
|
|
574
616
|
// TODO: assert something meaningful about the rendered output.
|
|
575
617
|
expect(screen).toBeDefined();
|
|
576
618
|
});
|
|
577
619
|
});
|
|
578
|
-
`},
|
|
620
|
+
`},To=e=>{let{hookName:r,sourceImportPath:t,withStore:o,testUtilsImportPath:n}=e;return o?`import { renderHook } from '@testing-library/react';
|
|
579
621
|
import { describe, expect, it } from 'vitest';
|
|
580
622
|
import { TestProviders } from '${n}';
|
|
581
623
|
import { ${r} } from '${t}';
|
|
@@ -596,7 +638,7 @@ describe('${r}', () => {
|
|
|
596
638
|
expect(result.current).toBeDefined();
|
|
597
639
|
});
|
|
598
640
|
});
|
|
599
|
-
`},
|
|
641
|
+
`},Eo=e=>{let{camelName:r,sourceImportPath:t}=e,o=`${r}Slice`;return`import { describe, expect, it } from 'vitest';
|
|
600
642
|
import { ${o}, resetState, setError, setLoading } from '${t}';
|
|
601
643
|
|
|
602
644
|
describe('${o}', () => {
|
|
@@ -621,64 +663,25 @@ describe('${o}', () => {
|
|
|
621
663
|
expect(next).toEqual(initialState);
|
|
622
664
|
});
|
|
623
665
|
});
|
|
624
|
-
`};var
|
|
666
|
+
`};var Q=async e=>{let{projectPath:r,sourceFile:t,kind:o,symbolName:n}=e,i=_e.dirname(t),s=_e.basename(t).replace(/\.(tsx?|jsx?)$/i,""),a=o==="component"?".tsx":".ts",p=_e.join(i,`${s}.test${a}`),u=`./${s}`,g=Ms(i,_e.join(r,"test","test-utils")),x;return o==="component"?x=Co({componentName:n,sourceImportPath:u,testUtilsImportPath:g,hasRedux:!!e.hasRedux,hasI18n:!!e.hasI18n}):o==="hook"?x=To({hookName:n,sourceImportPath:u,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?g:void 0}):x=Eo({camelName:n,sourceImportPath:u}),await f(p,x),p},Ms=(e,r)=>{let t=_e.relative(e,r).replace(/\\/g,"/");return t.startsWith(".")?t:`./${t}`};import Gs from"enquirer";var{prompt:Hs}=Gs,Ro=async e=>{let r=[];e||r.push({type:"input",name:"componentName",message:"Component name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Component name is required"}),r.push({type:"confirm",name:"isClient",message:"Add 'use client' directive?",initial:!1});let t=await Hs(r);return{componentName:e||t.componentName,isClient:t.isClient}};var $o=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 o=process.cwd();l(q.cyan(`
|
|
625
667
|
\u{1F9E9} Component Generator
|
|
626
|
-
`)),h.start("Detecting project setup...");let n=await
|
|
668
|
+
`)),h.start("Detecting project setup...");let n=await I(o);h.succeed("Project setup detected");let i=await Ro(r),s=$(i.componentName),a=t.client??i.isClient,p=t.i18n??!1,u=Us(t,n.hasTests);h.start("Generating component..."),await Ft({name:i.componentName,projectPath:o,isClient:a||p,hasI18n:p,featurePath:t.feature}),h.succeed("Component generated");let g=null;if(u){let y=t.feature?Bt.join(o,t.feature,"components",`${s}.tsx`):Bt.join(o,"src/components/common",s,`${s}.tsx`);h.start("Generating test..."),g=await Q({projectPath:o,sourceFile:y,kind:"component",symbolName:s,hasRedux:n.hasRedux,hasI18n:n.hasI18n}),h.succeed("Test generated")}let x=t.feature?`${t.feature}/components/${s}.tsx`:`src/components/common/${s}/${s}.tsx`;l(q.green(`
|
|
627
669
|
\u2728 Component '${s}' created successfully!
|
|
628
|
-
`)),
|
|
629
|
-
|
|
630
|
-
${e}`},Pt=(e,r,t)=>{let o=/combineReducers\(\{([^}]*)\}\)/s,n=e.match(o);if(!n)throw new Error("Could not find combineReducers in rootReducer.ts");let i=n[1],s=`
|
|
631
|
-
${r}: ${t},`,c=i.trimEnd()+s;return e.replace(o,`combineReducers({${c}
|
|
632
|
-
})`)},Ee=(e,r)=>{let t=e.lastIndexOf("} as const;");if(t===-1)throw new Error("Could not find closing brace of AppApis object");let o=e.substring(0,t),n=e.substring(t);return`${o.trimEnd()}
|
|
633
|
-
${r}
|
|
634
|
-
${n}`};var Ie=async e=>{let{serviceName:r,projectPath:t}=e,o=$(r),n=vn.join(t,"src","lib","config","app-apis.ts");try{let i=await Sn(n,"utf-8");if(i.includes(`${o}:`))return;if(!i.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let s=` ${o}: {
|
|
635
|
-
base: \`\${API_PREFIX}/${r}\`,
|
|
636
|
-
getAll: \`\${API_PREFIX}/${r}\`,
|
|
637
|
-
},`;i=Ee(i,s),await wn(n,i)}catch(i){throw new Error(`Failed to register API endpoints: ${i}`,{cause:i})}};import{readFile as Cn,writeFile as En}from"node:fs/promises";import In from"node:path";T();var Tt=async(e,r,t)=>{let o=In.join(e,"src","lib","config","app-paths.ts");if(!l(o))return;let n=await Cn(o,"utf-8"),i=$(r);if(n.includes(`${i}:`))return;let s=n.lastIndexOf("} as const;");if(s===-1)return;let c=` ${i}: '${t}',
|
|
638
|
-
`;n=n.slice(0,s)+c+n.slice(s),await En(o,n)};import{readFile as Rn,writeFile as Nn}from"node:fs/promises";import bn from"node:path";var Re=async e=>{let{projectPath:r,name:t,persist:o,importPath:n}=e,i=bn.join(r,"src","store","rootReducer.ts");try{let s=await Rn(i,"utf-8"),c=$(t),p=`${c}Reducer`,f=`${c}PersistConfig`,g=n.replace(/^src\//,"@/"),y=o?`import { ${p}, ${f} } from '${g}';`:`import { ${p} } from '${g}';`;s=yt(s,y);let x=o?`persistReducer(${f}, ${p})`:p;s=Pt(s,c,x),await Nn(i,s)}catch(s){throw new Error(`Failed to register reducer '${t}' in rootReducer: ${s}`,{cause:s})}};T();import{readFile as $n,writeFile as An}from"node:fs/promises";import On from"node:path";var St=async(e,r)=>{let t=On.join(e,"src","i18n","translations","en.json");if(!l(t))return;let o=await $n(t,"utf-8"),n=JSON.parse(o);n[r]||(n[r]={title:r,description:`${r} page description`},await An(t,`${JSON.stringify(n,null,2)}
|
|
639
|
-
`))};var wt=()=>[{name:"Project setup detected",spinnerText:"Detecting project setup...",execute:async e=>{e.detection=await O(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 Ie({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 Re({projectPath:e.projectPath,name:e.featureName,persist:e.persistStore,importPath:_n.join(e.basePath,e.featureName,"store")})}}];var me=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(o){throw r.spinner.fail(t.name),o}}};var vt=()=>[{name:"Service files generated",spinnerText:"Generating service files...",execute:async e=>{await ye({name:e.serviceName,outputPath:e.servicePath,httpClient:e.httpClient})}},{name:"API endpoints registered",spinnerText:"Registering API endpoints...",execute:async e=>{await Ie({serviceName:e.serviceName,projectPath:e.projectPath})}}];import kn from"node:path";var Ct=()=>[{name:"Slice files generated",spinnerText:"Generating slice files...",execute:async e=>{await Se({name:e.sliceName,outputPath:e.slicePath,persist:e.persistSlice})}},{name:"Slice registered in rootReducer",spinnerText:"Registering slice in rootReducer...",execute:async e=>{await Re({projectPath:e.projectPath,name:e.sliceName,persist:e.persistSlice,importPath:kn.join(e.basePath,e.sliceName)})}}];import jn from"enquirer";var{prompt:Fn}=jn,Yr=async(e,r,t,o,n,i,s)=>{let c=[];e||c.push({type:"input",name:"featureName",message:"What is the feature name?",initial:"my-feature",validate:f=>/^[a-z0-9-]+$/.test(f)?!0:"Feature name must be lowercase and contain only alphanumeric characters and hyphens."}),r&&o===void 0&&n===void 0&&(c.push({type:"confirm",name:"createStore",message:"Generate Redux store for this feature?",initial:!0}),c.push({type:"confirm",name:"persistStore",message:"Enable persistence for this store?",initial:!1,skip(){return!this.state.answers.createStore}})),t&&t!=="none"&&i===void 0&&s===void 0&&(c.push({type:"confirm",name:"createService",message:"Generate API service for this feature?",initial:!0}),t==="both"&&c.push({type:"select",name:"selectedHttpClient",message:"Which HTTP client to use for the service?",choices:["fetch","axios"],initial:0,skip(){return!this.state.answers.createService}}));let p=c.length>0?await Fn(c):{};return{featureName:e||p.featureName,hasRedux:r||!1,createStore:o===!0?!1:n!==void 0?!0:p.createStore||!1,persistStore:n==="persist"?!0:n==="no-persist"?!1:p.persistStore||!1,httpClient:t||"none",createService:i===!0?!1:s!==void 0?!0:p.createService||!1,selectedHttpClient:s||p.selectedHttpClient||(t==="both"?"fetch":t!=="none"?t:void 0)}};var Vr=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 o=process.cwd();Dn(t),m(k.cyan(`
|
|
640
|
-
\u{1F3AF} Feature Generator
|
|
641
|
-
`));let n=wt();h.start("Detecting project setup...");let{detectProjectSetup:i}=await Promise.resolve().then(()=>(U(),wr)),s=await i(o);h.succeed("Project setup detected"),m(k.dim(` Redux: ${s.hasRedux?"\u2713":"\u2717"}`)),m(k.dim(` HTTP Client: ${s.httpClient}`)),m(k.dim(` i18n: ${s.hasI18n?"\u2713":"\u2717"}
|
|
642
|
-
`));let c=await Yr(r,s.hasRedux,s.httpClient,t.skipStore,t.store,t.skipService,t.service),p=t.path||Et.join("src","features"),f=Et.join(o,p,c.featureName);await oe(o,c.featureName,p)&&(w(`Feature '${c.featureName}' already exists at ${p}!`),process.exit(1)),await me(n.slice(1),{projectPath:o,spinner:h,featureName:c.featureName,basePath:p,featurePath:f,createStore:c.createStore,persistStore:c.persistStore,createService:c.createService,httpClient:c.selectedHttpClient,detection:s}),Ln(c,p)}catch(o){h.fail("Feature generation failed"),w(`${o}`),process.exit(1)}})},Dn=e=>{e.store&&!["persist","no-persist"].includes(e.store)&&(w("Invalid --store option. Use: persist or no-persist"),process.exit(1)),e.service&&!["axios","fetch"].includes(e.service)&&(w("Invalid --service option. Use: axios or fetch"),process.exit(1)),e.skipStore&&e.store&&(w("Cannot use --skip-store and --store together"),process.exit(1)),e.skipService&&e.service&&(w("Cannot use --skip-service and --service together"),process.exit(1))},Ln=(e,r)=>{let t=Et.join(r,e.featureName);m(k.green(`
|
|
643
|
-
\u2728 Feature '${e.featureName}' created successfully!
|
|
644
|
-
`)),m(k.dim("Generated files:")),m(k.dim(` \u{1F4C2} ${t}/`)),m(k.dim(" \u251C\u2500\u2500 components/")),m(k.dim(" \u251C\u2500\u2500 hooks/")),m(k.dim(" \u251C\u2500\u2500 types/")),e.createStore&&m(k.dim(" \u251C\u2500\u2500 store/")),e.createService&&m(k.dim(" \u251C\u2500\u2500 services/")),m(k.dim(` \u2514\u2500\u2500 index.ts
|
|
645
|
-
`)),m(k.cyan("Next steps:"));let o=r.replace(/^src\//,"@/");m(k.dim(` 1. Import and use the feature: import { ${e.featureName} } from '${o}/${e.featureName}'`)),e.createStore&&m(k.dim(` 2. Customize your Redux slice in: ${t}/store/`)),e.createService&&m(k.dim(` 3. Add API methods in: ${t}/services/${e.featureName}.service.ts`)),m("")};import It from"node:path";import ge from"picocolors";U();import Gn from"enquirer";var{prompt:Hn}=Gn,Kr=async e=>e?{hookName:e}:await Hn({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 Wr=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 o=process.cwd();m(ge.cyan(`
|
|
646
|
-
\u{1FA9D} Hook Generator
|
|
647
|
-
`));let n=await O(o),i=await Kr(r),c=`use${R(i.hookName)}`,p=Un(t,n.hasTests);h.start("Generating hook..."),await ut({name:i.hookName,projectPath:o,isClient:t.client??!0,featurePath:t.feature}),h.succeed("Hook generated");let f=t.feature?It.join(o,t.feature,"hooks",`${c}.ts`):It.join(o,"src/hooks",`${c}.ts`),g=null;p&&(h.start("Generating test..."),g=await q({projectPath:o,sourceFile:f,kind:"hook",symbolName:c,hookUsesStore:n.hasRedux&&!!t.feature}),h.succeed("Test generated"));let y=t.feature?`${t.feature}/hooks/${c}.ts`:`src/hooks/${c}.ts`;m(ge.green(`
|
|
648
|
-
\u2728 Hook '${c}' created successfully!
|
|
649
|
-
`)),m(ge.dim(` \u{1F4C4} ${y}`)),g&&m(ge.dim(` \u{1F9EA} ${It.relative(o,g)}`)),m(""),m(ge.cyan("Usage:"));let x=t.feature?`${t.feature.replace(/^src\//,"@/")}/hooks/${c}`:`@/hooks/${c}`;m(ge.dim(` import { ${c} } from '${x}';`)),m("")}catch(o){h.fail("Hook generation failed"),w(`${o}`),process.exit(1)}})},Un=(e,r)=>e.noTest?!1:e.test?!0:r;import zn from"node:path";import K from"picocolors";T();U();import Mn from"enquirer";var{prompt:Jn}=Mn,qr=async e=>{let r=[];e||r.push({type:"input",name:"code",message:"Locale code (e.g., es, fr, de):",validate:o=>o?/^[a-z]{2}(-[A-Z]{2})?$/.test(o)?!0:"Use format: xx or xx-XX (e.g., es, pt-BR)":"Locale code is required"}),r.push({type:"input",name:"name",message:"Language name (e.g., Spanish, French):",validate:o=>o?!0:"Language name is required"},{type:"input",name:"country",message:"Country (e.g., Spain, France):",validate:o=>o?!0:"Country is required"},{type:"input",name:"flag",message:"Flag emoji (e.g., \u{1F1EA}\u{1F1F8}, \u{1F1EB}\u{1F1F7}):",validate:o=>o?!0:"Flag emoji is required"},{type:"confirm",name:"copyTranslations",message:"Copy translations from English? (No = empty values)",initial:!1});let t=await Jn(r);return{code:e||t.code,name:t.name,country:t.country,flag:t.flag,copyTranslations:t.copyTranslations}};var Zr=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 o=process.cwd();m(K.cyan(`
|
|
650
|
-
\u{1F30D} Locale Generator
|
|
651
|
-
`)),h.start("Detecting project setup...");let n=await O(o);h.succeed("Project setup detected"),n.hasI18n||(h.fail("i18n is not set up in this project"),w("Please set up internationalization first"),m(K.dim(`
|
|
652
|
-
Run: npx @teispace/next-maker setup --i18n
|
|
653
|
-
`)),process.exit(1));let i=await qr(r),s=zn.join(o,"src","i18n","translations",`${i.code}.json`);l(s)&&(w(`Locale '${i.code}' already exists!`),process.exit(1)),h.start("Adding locale..."),await ft({...i,projectPath:o,copyTranslations:t.copyTranslations??i.copyTranslations}),h.succeed("Locale added"),m(K.green(`
|
|
654
|
-
\u2728 Locale '${i.code}' (${i.name}) added successfully!
|
|
655
|
-
`)),m(K.dim("Updated files:")),m(K.dim(` \u{1F4C4} src/i18n/translations/${i.code}.json`)),m(K.dim(" \u{1F4DD} src/types/i18n.ts (SupportedLocale type)")),m(K.dim(" \u{1F4DD} src/lib/config/app-locales.ts")),m(""),m(K.cyan("Next steps:")),m(K.dim(` 1. Translate: src/i18n/translations/${i.code}.json`)),m(K.dim(` 2. Visit: http://localhost:3000/${i.code}`)),m("")}catch(o){h.fail("Locale generation failed"),w(`${o}`),process.exit(1)}})};import eo from"node:path";import J from"picocolors";U();import Bn from"enquirer";var{prompt:Xn}=Bn,Qr=async e=>{let r=[];e||r.push({type:"input",name:"pageName",message:"Page name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Page name is required"}),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 Xn(r);return{pageName:e||t.pageName,withLoading:t.withLoading,withError:t.withError}};var to=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 o=process.cwd();m(J.cyan(`
|
|
656
|
-
\u{1F4C4} Page Generator
|
|
657
|
-
`)),h.start("Detecting project setup...");let n=await O(o);h.succeed("Project setup detected"),m(J.dim(` i18n: ${n.hasI18n?"\u2713":"\u2717"}
|
|
658
|
-
`));let i=await Qr(r),s=i.pageName,c=R(s),p=n.hasI18n?"src/app/[locale]":"src/app";await oe(o,s,p)&&(w(`Page '${s}' already exists at ${p}/${s}!`),process.exit(1)),h.start("Generating page files..."),await ht({name:s,projectPath:o,hasI18n:n.hasI18n,dynamic:t.dynamic,withLoading:t.loading??i.withLoading,withError:t.error??i.withError}),h.succeed("Page files generated"),h.start("Registering route...");let g=t.dynamic?`/${s}/[${t.dynamic}]`:`/${s}`;await Tt(o,s,g),h.succeed("Route registered"),n.hasI18n&&(h.start("Adding translation namespace..."),await St(o,c),h.succeed("Translation namespace added"));let y=t.dynamic?eo.join(p,s,`[${t.dynamic}]`):eo.join(p,s);m(J.green(`
|
|
659
|
-
\u2728 Page '${s}' created successfully!
|
|
660
|
-
`)),m(J.dim("Generated files:")),m(J.dim(` \u{1F4C2} ${y}/`)),m(J.dim(" \u251C\u2500\u2500 page.tsx")),(t.loading??i.withLoading)&&m(J.dim(" \u251C\u2500\u2500 loading.tsx")),(t.error??i.withError)&&m(J.dim(" \u2514\u2500\u2500 error.tsx")),m(""),n.hasI18n&&(m(J.cyan("Next steps:")),m(J.dim(` 1. Add translations in: src/i18n/translations/en.json \u2192 "${c}"`)),m(J.dim(` 2. Visit: http://localhost:3000/${s}`)),m(""))}catch(o){h.fail("Page generation failed"),w(`${o}`),process.exit(1)}})};import{existsSync as Wn}from"node:fs";import{mkdir as qn}from"node:fs/promises";import Ne from"node:path";import z from"picocolors";U();import{readFile as Yn,writeFile as Vn}from"node:fs/promises";import Kn from"node:path";var ro=async(e,r)=>{let t=$(r),o=Kn.join(e,"src","lib","config","app-apis.ts");try{let n=await Yn(o,"utf-8");if(n.includes(`${t}:`))return;if(!n.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let i=Ir({camelName:t,kebabName:r});n=Ee(n,i),await Vn(o,n)}catch(n){throw new Error(`Failed to register CRUD API endpoints: ${n}`,{cause:n})}};import oo from"enquirer";var io=async(e,r,t,o)=>{let n=e||(await oo.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,i;return r!==void 0?i="axios":t!==void 0?i="fetch":o==="axios"?i="axios":o==="fetch"?i="fetch":o==="both"?i=(await oo.prompt({type:"select",name:"httpClient",message:"Choose HTTP client:",choices:["axios","fetch"]})).httpClient:i="axios",{serviceName:n,httpClient:i}};var no=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 o=process.cwd();t.axios&&t.fetch&&(w("Cannot use --axios and --fetch together"),process.exit(1)),m(z.cyan(`
|
|
661
|
-
\u{1F527} Service Generator
|
|
662
|
-
`)),h.start("Detecting project setup...");let n=await O(o);h.succeed("Project setup detected"),n.httpClient==="none"&&(h.fail("No HTTP client is setup in this project"),w("Please setup either AxiosClient or FetchClient first"),m(z.dim(`
|
|
663
|
-
Check: lib/utils/http/axios-client or lib/utils/http/fetch-client
|
|
664
|
-
`)),process.exit(1));let i=[];(n.httpClient==="axios"||n.httpClient==="both")&&i.push("Axios \u2713"),(n.httpClient==="fetch"||n.httpClient==="both")&&i.push("Fetch \u2713"),m(z.dim(` HTTP Clients: ${i.join(", ")}
|
|
665
|
-
`));let s=await io(r,t.axios,t.fetch,n.httpClient),{basePath:c,servicePath:p}=Zn(o,s.serviceName,t.path);Wn(p)||await qn(p,{recursive:!0}),De(o,c,`${s.serviceName}.service.ts`)&&(w(`Service '${s.serviceName}' already exists at ${c}!`),process.exit(1)),t.crud?(h.start("Generating CRUD service files..."),await lt({name:s.serviceName,outputPath:p,httpClient:s.httpClient}),h.succeed("CRUD service files generated"),h.start("Registering CRUD API endpoints..."),await ro(o,s.serviceName),h.succeed("CRUD API endpoints registered")):await me(vt(),{projectPath:o,spinner:h,serviceName:s.serviceName,servicePath:p,httpClient:s.httpClient}),Qn(s,c,!!t.crud)}catch(o){h.fail("Service generation failed"),w(`${o}`),process.exit(1)}})},Zn=(e,r,t)=>{let o;if(t){let n=t.replace(/^src\//,"");if(n.startsWith("features/")){let i=n.split("/")[1];o=Ne.join("src","features",i,"services")}else o=Ne.join("src",n)}else o=Ne.join("src","features",r,"services");return{basePath:o,servicePath:Ne.join(e,o)}},Qn=(e,r,t=!1)=>{let o=Ne.join(r,`${e.serviceName}.service.ts`);m(z.green(`
|
|
666
|
-
\u2728 ${t?"CRUD ":""}Service '${e.serviceName}' created successfully!
|
|
667
|
-
`)),m(z.dim("Generated files:")),m(z.dim(` \u{1F4C4} ${o}
|
|
668
|
-
`)),m(z.cyan("Next steps:"));let n=r.replace(/^src\//,"@/");m(z.dim(` 1. Import service: import { ${e.serviceName}Service } from '${n}/${e.serviceName}.service'`)),t?(m(z.dim(" 2. Available methods: getAll, getById, create, update, delete")),m(z.dim(" 3. Update DTO types in the service file"))):m(z.dim(` 2. Use in component: const data = await ${e.serviceName}Service.getAll()`)),m("")};import Ts from"enquirer";import Gt from"picocolors";C();import so from"node:path";import es from"picocolors";T();var Rt=async e=>{let r=I("Adding @next/bundle-analyzer...");try{let t=so.join(e,a.NEXT_CONFIG);if(!l(t))throw new Error(`${a.NEXT_CONFIG} not found.`);let o=await d(t);if(o.includes("@next/bundle-analyzer")){r.fail("@next/bundle-analyzer is already configured.");return}o=`import withBundleAnalyzer from '@next/bundle-analyzer';
|
|
669
|
-
${o}`;let n=/export default (.*?);/,i=o.match(n);if(!i)throw new Error("Could not locate `export default` in next.config.ts.");let c=`const bundleAnalyzer = withBundleAnalyzer({
|
|
670
|
+
`)),l(q.dim(` \u{1F4C4} ${x}`)),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} ${Bt.relative(o,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(o){h.fail("Component generation failed"),v(`${o}`),process.exit(1)}})},Us=(e,r)=>e.noTest?!1:e.test?!0:r;import j from"picocolors";w();P();import Vs from"node:path";w();import bo from"node:path";import zs from"picocolors";P();var Fe=async e=>{let r=R("Adding @next/bundle-analyzer...");try{let t=bo.join(e,c.NEXT_CONFIG);if(!m(t))throw new Error(`${c.NEXT_CONFIG} not found.`);let o=await d(t);if(o.includes("@next/bundle-analyzer")){r.fail("@next/bundle-analyzer is already configured.");return}o=`import withBundleAnalyzer from '@next/bundle-analyzer';
|
|
671
|
+
${o}`;let n=/export default (.*?);/,i=o.match(n);if(!i)throw new Error("Could not locate `export default` in next.config.ts.");let a=`const bundleAnalyzer = withBundleAnalyzer({
|
|
670
672
|
enabled: process.env.ANALYZE === 'true',
|
|
671
673
|
});
|
|
672
674
|
|
|
673
|
-
export default bundleAnalyzer(${i[1]});`;o=o.replace(n,
|
|
674
|
-
`,await
|
|
675
|
+
export default bundleAnalyzer(${i[1]});`;o=o.replace(n,a),await f(t,o),await k(bo.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 rt(e,"@next/bundle-analyzer"),r.succeed(zs.green("@next/bundle-analyzer configured."))}catch(t){throw r.fail("Failed to add @next/bundle-analyzer."),t}};var No={id:"bundle-analyzer",name:"Bundle Analyzer",description:"@next/bundle-analyzer wrapped around the default export",detect:async e=>{let r=Vs.join(e,c.NEXT_CONFIG);return m(r)?/@next\/bundle-analyzer/.test(await d(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/}],apply:Fe};w();P();import Xs from"node:path";w();import Io from"node:path";import Js from"picocolors";P();var Xt=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}},Yt=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 Bs=`${JSON.stringify({path:"cz-conventional-changelog"},null,2)}
|
|
676
|
+
`,je=async e=>{let r=R("Setting up Commitizen...");try{let t=Io.join(e,c.CZRC);m(t)||await f(t,Bs);let o=Io.join(e,c.PACKAGE_JSON),n=JSON.parse(await d(o)),i=Yt(n);if(await k(o,s=>Xt(s)),i.length>0){r.text=`Installing ${i.join(", ")}...`;let s=await O(e);await ue(e,s,i)}r.succeed(Js.green("Commitizen configured. Use `commit` to start a guided commit."))}catch(t){throw r.fail("Failed to set up Commitizen."),t}};var Ao={id:"commitizen",name:"Commitizen",description:"Guided conventional-commit prompt via `commit` script",detect:async e=>m(Xs.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:je};w();P();import Ws from"node:path";import Ks from"node:path";import qs from"picocolors";P();w();P();import ko from"node:path";import Ys from"degit";var Oo=async(e,r)=>{r.text="Fetching assets from starter repo...",await Ys("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},_o=async(e,r)=>{let t=ko.join(e,c.THEME_PROVIDER),o=ko.join(r,"src/providers/CustomThemeProvider.tsx");await _(o,t)};w();P();import De from"node:path";w();P();import Fo from"node:path";var lt=async e=>{let r=[Fo.join(e,c.ROOT_LAYOUT),Fo.join(e,"src/app/[locale]/layout.tsx")];for(let t of r)if(m(t)&&(await d(t)).includes("<body"))return t;for(let t of r)if(m(t))return t;return""};var jo=async e=>{let r=De.join(e,c.THEME_PROVIDER),t=De.join(e,c.GLOBALS_CSS),o=De.join(e,"package.json");if(m(r))return{isSetup:!0,reason:"CustomThemeProvider.tsx exists"};if(m(o)){let n=JSON.parse(await d(o)),i="@teispace/next-themes";if(n.dependencies?.[i]||n.devDependencies?.[i])return{isSetup:!0,reason:`${i} is installed`}}return m(t)&&(await d(t)).includes("@custom-variant dark")?{isSetup:!0,reason:"Dark theme CSS found in globals.css"}:{isSetup:!1,reason:""}},Do=async e=>{let r=De.join(e,c.GLOBALS_CSS),t=De.join(e,c.PROVIDERS_INDEX),o=await lt(e);if(!m(r)||!o||!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 o};w();P();import mt from"node:path";var Lo=async e=>{let r=mt.join(e,c.PROVIDERS_INDEX),t=await d(r);t.includes("CustomThemeProvider")||(t+=`export * from './CustomThemeProvider';
|
|
677
|
+
`,await f(r,t))},Mo=async e=>{let r=mt.join(e,"src/providers/RootProvider.tsx");if(!m(r))return;let t=await d(r),o="'use client';",n=t.includes(o);if(n&&(t=t.replace(o,"").trim()),t.includes("CustomThemeProvider")||(/import\s*\{[^}]*\}\s*from\s*'@\/providers'/.test(t)?t=t.replace(/(import\s*\{)([^}]*)(\}\s*from\s*'@\/providers')/,(i,s,a,p)=>`${s}${a.trim()?`${a.trim()}, CustomThemeProvider `:" CustomThemeProvider "}${p}`):t=`import { CustomThemeProvider } from '@/providers';
|
|
675
678
|
${t}`),n&&(t=`${o}
|
|
676
679
|
${t}`),!t.includes("<CustomThemeProvider"))if(t.includes("{children}"))t=t.replace(/\{children\}/,"<CustomThemeProvider>{children}</CustomThemeProvider>");else{let i=t.match(/return\s*\(\s*([\s\S]*?)\s*\);/);i&&(t=t.replace(i[0],`return (
|
|
677
680
|
<CustomThemeProvider>
|
|
678
681
|
${i[1]}
|
|
679
682
|
</CustomThemeProvider>
|
|
680
|
-
);`))}await
|
|
681
|
-
${
|
|
683
|
+
);`))}await f(r,t)},Go=async(e,r)=>{let t=mt.join(e,c.GLOBALS_CSS),o=mt.join(r,"src/styles/globals.css"),n="";if(m(o)){let s=await d(o),a=s.match(/@custom-variant dark \(.*?\);/),p=s.match(/@theme \{[\s\S]*?\}/);a&&(n+=`
|
|
684
|
+
${a[0]}
|
|
682
685
|
`),p&&(n+=`
|
|
683
686
|
${p[0]}
|
|
684
687
|
`)}n||(n=`
|
|
@@ -688,61 +691,61 @@ ${p[0]}
|
|
|
688
691
|
--color-dark: #202938;
|
|
689
692
|
--color-light: #f5f5f5;
|
|
690
693
|
}
|
|
691
|
-
`);let i=await d(t);if(!i.includes("@custom-variant dark")){if(i.includes("@import")){let s=i.lastIndexOf("@import"),
|
|
692
|
-
`,s);i=i.slice(0,
|
|
694
|
+
`);let i=await d(t);if(!i.includes("@custom-variant dark")){if(i.includes("@import")){let s=i.lastIndexOf("@import"),a=i.indexOf(`
|
|
695
|
+
`,s);i=i.slice(0,a+1)+n+i.slice(a+1)}else i=n+i;await f(t,i)}},Ho=async e=>{let r=await d(e);r.includes("dark:bg-dark")||(r=r.replace(/className="([^"]*)"/,'className="$1 bg-light dark:bg-dark"'),await f(e,r))};var Le=async e=>{let r=R("Setting up Dark Theme..."),t=Ks.join(e,".next-maker-temp");try{let{isSetup:o,reason:n}=await jo(e);if(o){r.fail(`Dark Theme is already set up (${n}).`);return}let i=await Do(e);await Oo(t,r),await _o(e,t),await Lo(e),await Mo(e),await Go(e,t),await Ho(i),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(qs.green("Dark Theme setup successfully!"))}catch(o){throw r.fail("Failed to setup Dark Theme."),o}finally{await S(t)}};var Uo={id:"dark-theme",name:"Dark Theme",description:"@teispace/next-themes + CustomThemeProvider",detect:async e=>m(Ws.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/}],apply:Le};import Qs from"node:fs/promises";import dt from"node:path";import ut from"enquirer";import Kt from"picocolors";P();w();P();import ee from"node:fs/promises";import E from"node:path";import Zs from"degit";var zo=async(e,r)=>{r.text="Downloading assets from starter repository...",await Zs("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},Vo=async(e,r,t,o)=>{let n=E.join(e,c.HTTP_UTILS),i=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 u=await d(a),g=!1;u.includes("API_RESPONSE_DATA_KEY")||(u+=`
|
|
693
696
|
export const API_RESPONSE_DATA_KEY = 'data';
|
|
694
|
-
`,g=!0),
|
|
697
|
+
`,g=!0),u.includes("SAVE_AUTH_TOKENS")||(u+=`
|
|
695
698
|
export const SAVE_AUTH_TOKENS = false;
|
|
696
|
-
`,g=!0),g&&await
|
|
699
|
+
`,g=!0),g&&await f(a,u)}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(i,"token-store.ts"),E.join(n,"token-store.ts")),await _(E.join(i,"client-utils.ts"),E.join(n,"client-utils.ts")),o&&o.length>0)for(let u of o){let g=E.join(e,u==="fetch"?c.FETCH_CLIENT:c.AXIOS_CLIENT);await S(g)}for(let u of t)u==="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(i,"index.ts"),E.join(n,"index.ts")),await _(E.join(r,c.HTTP_TYPES),E.join(e,c.HTTP_TYPES))},Jo=async e=>{let{isFileUsed:r,isStringUsed:t}=await Promise.resolve().then(()=>(ot(),Ir));await S(E.join(e,c.HTTP_UTILS));let o=E.join(e,c.STORAGE_SERVICE);m(o)&&(await t(e,"SecureStorageService",[o])||await S(o));let n=E.join(e,c.CONSTANTS);if(m(n)){let p=await d(n),u=!1;await t(e,"API_RESPONSE_DATA_KEY",[n])||(p=p.replace(/export const API_RESPONSE_DATA_KEY = .*;\n?/g,""),u=!0),await t(e,"SAVE_AUTH_TOKENS",[n])||(p=p.replace(/export const SAVE_AUTH_TOKENS = .*;\n?/g,""),u=!0),u&&(p=`${p.replace(/\n\s*\n/g,`
|
|
697
700
|
|
|
698
701
|
`).trim()}
|
|
699
|
-
`,await
|
|
702
|
+
`,await f(n,p))}let i=E.join(e,c.ERRORS_DIR);if(m(i)){let p=await ee.readdir(i),u=!1;for(let g of p){let x=E.parse(g).name;if(await r(e,x,[E.join(i,g)])){u=!0;break}}u||await S(i)}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 Me from"node:path";var Bo=async e=>{let r=Me.join(e,c.FETCH_CLIENT_FILE),t=Me.join(e,c.AXIOS_CLIENT_FILE),o=m(r),n=m(t);return o&&n?{status:"both",reason:"Both Fetch and Axios clients are detected"}:o?{status:"fetch",reason:"Fetch client is already setup"}:n?{status:"axios",reason:"Axios client is already setup"}:{status:"none"}},Xo=async e=>{let t=[Me.join(e,"src/lib"),Me.join(e,"src/types"),Me.join(e,"src/lib/config")].filter(o=>!m(o));if(t.length>0)throw new Error(`Invalid project structure. Missing directories:
|
|
700
703
|
${t.map(o=>`- ${o}`).join(`
|
|
701
|
-
`)}`)};
|
|
704
|
+
`)}`)};ot();var Ge=async e=>{let r=R("Checking HTTP Client setup..."),t=dt.join(e,".next-maker-temp-http");try{let{status:o,reason:n}=await Bo(e);r.stop();let i=await ea(o,n);if(i.type==="cancel"){console.log(Kt.yellow("Setup cancelled."));return}if(r.start("Setting up HTTP client(s)..."),await Xo(e),await zo(t,r),i.type==="replace"&&i.fromClient&&(r.text="Migrating client usages...",await bt(e,i.fromClient,i.clients[0])),i.type!=="remove-both"&&i.type!=="remove-one"){r.text="Copying client files...";let Y=i.type==="replace"&&i.fromClient?[i.fromClient]:void 0;await Vo(e,t,i.clients,Y)}if(i.type==="remove-one"||i.type==="remove-both"){r.text="Removing client files...";let{PROJECT_PATHS:Y}=await Promise.resolve().then(()=>(w(),Rr));for(let Qe of i.clients){let et=dt.join(e,Qe==="fetch"?Y.FETCH_CLIENT:Y.AXIOS_CLIENT);await S(et)}Yo(o,i).length===0&&(r.text="Performing full cleanup...",await Jo(e),await Nt(e))}let s=Yo(o,i);s.length>0&&(r.text="Configuring client...",await Tt(e,s),await Et(e),await Rt(e),await $t(e)),await ve(e,s),r.text="Managing dependencies...";let a=dt.join(e,"package.json"),p=JSON.parse(await Qs.readFile(a,"utf-8")),u={...p.dependencies,...p.devDependencies};s.includes("axios")?u.axios||(r.text="Installing Axios...",await z(e,"axios")):u.axios&&(r.text="Uninstalling Axios...",await wt(e,"axios"));let g=dt.join(e,"src/services/storage");s.length>0||m(g)?u["react-secure-storage"]||(r.text="Installing react-secure-storage...",await z(e,"react-secure-storage")):u["react-secure-storage"]&&(r.text="Uninstalling react-secure-storage...",await wt(e,"react-secure-storage")),r.text="Formatting code...";let y=await O(e);await U(e,y,"lint:fix"),r.succeed(Kt.green(ta(i)))}catch(o){throw r.fail("Failed to setup HTTP Client."),o}finally{await S(t)}};async function ea(e,r){if(e==="none"){let{choice:o}=await ut.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 o==="both"?{type:"install",clients:["fetch","axios"]}:{type:"install",clients:[o]}}if(e==="fetch"){let{action:o}=await ut.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(o==="cancel")return{type:"cancel",clients:[]};if(o==="add")return{type:"add",clients:["axios"]};if(o==="replace")return{type:"replace",clients:["axios"],fromClient:"fetch"};if(o==="remove")return{type:"remove-one",clients:["fetch"]}}if(e==="axios"){let{action:o}=await ut.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(o==="cancel")return{type:"cancel",clients:[]};if(o==="add")return{type:"add",clients:["fetch"]};if(o==="replace")return{type:"replace",clients:["fetch"],fromClient:"axios"};if(o==="remove")return{type:"remove-one",clients:["axios"]}}console.log(Kt.yellow(`\u2716 ${r}`));let{action:t}=await ut.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 Yo(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 ta(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 Ko={id:"http-client",name:"HTTP Client",description:"Axios and/or Fetch client utilities",detect:async e=>{let{detectProjectSetup:r}=await Promise.resolve().then(()=>(L(),$e));return(await r(e)).httpClient!=="none"},files:[{path:"src/lib/utils/http",generated:!0,isDir:!0}],packages:[],scripts:[],injections:[],apply:Ge};w();import qt from"node:path";import na from"picocolors";P();w();P();import ra from"node:fs/promises";import se from"node:path";import oa from"degit";var qo=async(e,r)=>{r.text="Fetching assets from starter repo...",await oa("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},Wo=async(e,r)=>{let t=se.join(r,"src/i18n"),o=se.join(e,c.I18N_DIR);await ra.cp(t,o,{recursive:!0});let n=se.join(r,"src/proxy.ts"),i=se.join(e,c.PROXY);await _(n,i);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"),u=se.join(e,c.APP_LOCALES);await _(p,u)};w();P();import ft from"node:path";var Zo=async e=>{let r=ft.join(e,c.I18N_DIR),t=ft.join(e,"package.json");if(m(r))return{isSetup:!0,reason:"src/i18n directory exists"};if(m(t)){let o=JSON.parse(await d(t));if(o.dependencies?.["next-intl"]||o.devDependencies?.["next-intl"])return{isSetup:!0,reason:"next-intl is installed"}}return{isSetup:!1,reason:""}},Qo=async e=>{let r=ft.join(e,c.ROOT_LAYOUT),t=ft.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 ia from"node:fs/promises";import te from"node:path";var ei=async e=>{let r=te.join(e,c.NEXT_CONFIG);if(m(r)){let t=await d(r);if(!t.includes("createNextIntlPlugin")){t=`import createNextIntlPlugin from 'next-intl/plugin';
|
|
702
705
|
${t}`;let o=/export default (.*?);/;o.test(t)?t=t.replace(o,`
|
|
703
706
|
const withNextIntl = createNextIntlPlugin();
|
|
704
707
|
export default withNextIntl($1);`):(t+=`
|
|
705
708
|
const withNextIntl = createNextIntlPlugin();
|
|
706
709
|
`,t+=`export default withNextIntl(nextConfig);
|
|
707
|
-
`),await
|
|
708
|
-
`,await
|
|
709
|
-
`,await
|
|
710
|
+
`),await f(r,t)}}},ti=async e=>{let r=te.join(e,c.TYPES_INDEX);if(m(r)){let t=await d(r);t.includes("./i18n")||(t+=`export * from './i18n';
|
|
711
|
+
`,await f(r,t))}},ri=async e=>{let r=te.join(e,c.CONFIG_INDEX);if(m(r)){let t=await d(r);t.includes("./app-locales")||(t+=`export * from './app-locales';
|
|
712
|
+
`,await f(r,t))}},oi=async e=>{let r=te.join(e,"src/providers/RootProvider.tsx");if(m(r)){let t=await d(r),o="'use client';",n=t.includes(o);if(n&&(t=t.replace(o,"").trim()),t.includes("next-intl")||(t=`import { type AbstractIntlMessages, NextIntlClientProvider } from 'next-intl';
|
|
710
713
|
import type { SupportedLocale } from '@/types/i18n';
|
|
711
714
|
`+t),n&&(t=`${o}
|
|
712
715
|
${t}`),!t.includes("locale: SupportedLocale")){let i=` locale: SupportedLocale;
|
|
713
716
|
messages: AbstractIntlMessages;
|
|
714
|
-
timeZone: string;`,s=/(type\s+RootProviderProps\s*=\s*\{)([\s\S]*?)(\};?)/,
|
|
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,u,g,x)=>{let y=g.replace(/\s+$/,"");return`${u}${y}
|
|
715
718
|
${i}
|
|
716
|
-
${
|
|
717
|
-
${
|
|
719
|
+
${x}`}),t=t.replace(/export const RootProvider = \(\{([\s\S]*?)\}:\s*RootProviderProps\)/,(p,u)=>{let g=u.trim().replace(/,$/,"");return`export const RootProvider = ({ ${g?`${g}, `:""}locale, messages, timeZone }: RootProviderProps)`});else{let p=t.match(a);if(p){let u=p[1].trim().replace(/,$/,""),g=p[2].trim().replace(/;$/,""),x=`
|
|
720
|
+
${u?`${u},
|
|
718
721
|
`:""}locale,
|
|
719
722
|
messages,
|
|
720
|
-
timeZone,`,
|
|
723
|
+
timeZone,`,y=`
|
|
721
724
|
${g?`${g};
|
|
722
725
|
`:""}locale: SupportedLocale;
|
|
723
726
|
messages: AbstractIntlMessages;
|
|
724
|
-
timeZone: string;`;t=t.replace(p[0],`export const RootProvider = ({${
|
|
725
|
-
}: {${
|
|
726
|
-
}) => {`)}}}if(!t.includes("<NextIntlClientProvider")){let i=t.match(/return\s*(?:\(\s*)?([\s\S]*?)(?:\s*\))?;/);if(i){let s=i[1],
|
|
727
|
+
timeZone: string;`;t=t.replace(p[0],`export const RootProvider = ({${x}
|
|
728
|
+
}: {${y}
|
|
729
|
+
}) => {`)}}}if(!t.includes("<NextIntlClientProvider")){let i=t.match(/return\s*(?:\(\s*)?([\s\S]*?)(?:\s*\))?;/);if(i){let s=i[1],a=`<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
|
|
727
730
|
{children}
|
|
728
|
-
</NextIntlClientProvider>`;if(s.includes("{children}")){let p=s.replace("{children}",
|
|
731
|
+
</NextIntlClientProvider>`;if(s.includes("{children}")){let p=s.replace("{children}",a);t=t.replace(i[0],`return (
|
|
729
732
|
${p}
|
|
730
733
|
);`)}else t=t.replace(i[0],`return (
|
|
731
734
|
<NextIntlClientProvider locale={locale} messages={messages} timeZone={timeZone}>
|
|
732
735
|
${s}
|
|
733
736
|
</NextIntlClientProvider>
|
|
734
|
-
);`)}}await
|
|
737
|
+
);`)}}await f(r,t)}},ii=async e=>{let r=te.join(e,c.ROOT_LAYOUT),t=te.join(e,c.ROOT_PAGE),o=te.join(e,"src/app/[locale]");if(await ia.mkdir(o,{recursive:!0}),m(r)){let n=te.join(o,"layout.tsx"),i=await d(r);if(i.includes("next-intl")||(i=`import { routing } from '@/i18n/routing';
|
|
735
738
|
import { hasLocale } from 'next-intl';
|
|
736
739
|
import { notFound } from 'next/navigation';
|
|
737
740
|
import { getMessages, getTimeZone, setRequestLocale } from 'next-intl/server';
|
|
738
|
-
${i}`),!i.includes("generateStaticParams")){let p=i.indexOf("};",i.indexOf("export const metadata"));if(p!==-1){let
|
|
741
|
+
${i}`),!i.includes("generateStaticParams")){let p=i.indexOf("};",i.indexOf("export const metadata"));if(p!==-1){let u=p+2;i=i.slice(0,u)+`
|
|
739
742
|
|
|
740
743
|
export function generateStaticParams() {
|
|
741
744
|
return routing.locales.map((locale) => ({ locale }));
|
|
742
|
-
}`+i.slice(
|
|
745
|
+
}`+i.slice(u)}}i=i.replace(/Readonly<\{\s*children:\s*React\.ReactNode;\s*}>/,`Readonly<{
|
|
743
746
|
children: React.ReactNode;
|
|
744
747
|
params: Promise<{ locale: string }>;
|
|
745
|
-
}>`);let s=/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,
|
|
748
|
+
}>`);let s=/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,a=i.match(s);if(a){a[1]||(i=i.replace("export default function","export default async function")),i=i.replace(/export default (async )?function RootLayout\(\{\s*children,?\s*\}\s*:/,"export default async function RootLayout({ children, params }:");let u=/export default async function RootLayout[\s\S]*?\)\s*\{/,g=i.match(u);if(g&&g.index!==void 0){let x=g.index+g[0].length-1;i=i.slice(0,x+1)+`
|
|
746
749
|
const { locale } = await params;
|
|
747
750
|
|
|
748
751
|
if (!hasLocale(routing.locales, locale)) {
|
|
@@ -752,17 +755,17 @@ export function generateStaticParams() {
|
|
|
752
755
|
setRequestLocale(locale);
|
|
753
756
|
|
|
754
757
|
const [messages, timeZone] = await Promise.all([getMessages(), getTimeZone()]);
|
|
755
|
-
`+i.slice(
|
|
758
|
+
`+i.slice(x+1)}i=i.replace(/<RootProvider>/,"<RootProvider locale={locale} messages={messages} timeZone={timeZone}>"),i=i.replace(/<html lang="en"/,"<html lang={locale}")}await f(n,i),await T(r)}if(m(t)){let n=te.join(o,"page.tsx"),i=await d(t);i.includes("next-intl")||(i=`import { SupportedLocale } from '@/types/i18n';
|
|
756
759
|
import { setRequestLocale } from 'next-intl/server';
|
|
757
760
|
${i}`),i=i.replace(/export default function Home\(\)/,`type Props = {
|
|
758
761
|
params: Promise<{ locale: string }>;
|
|
759
762
|
};
|
|
760
763
|
|
|
761
|
-
export default async function Home(props: Props)`);let s=i.indexOf("export default async function Home(props: Props) {");if(s!==-1){let
|
|
764
|
+
export default async function Home(props: Props)`);let s=i.indexOf("export default async function Home(props: Props) {");if(s!==-1){let a=`
|
|
762
765
|
const locale = (await props.params).locale as SupportedLocale;
|
|
763
766
|
setRequestLocale(locale);
|
|
764
|
-
`,p=i.indexOf("{",s);i=i.slice(0,p+1)+
|
|
765
|
-
reactCompiler: true,`),await
|
|
767
|
+
`,p=i.indexOf("{",s);i=i.slice(0,p+1)+a+i.slice(p+1)}await f(n,i),await T(t)}};var He=async e=>{let r=R("Setting up Internationalization (next-intl)..."),t=qt.join(e,".next-maker-temp-i18n");try{let{isSetup:o,reason:n}=await Zo(e);if(o){r.fail(`i18n is already set up (${n}).`);return}await Qo(e),await qo(t,r),r.text="Copying i18n files...",await Wo(e,t),r.text="Configuring project structure...",await ei(e),await ti(e),await ri(e),await oi(e),r.text="Migrating to [locale] structure...",await ii(e),r.text="Installing dependencies...",await z(e,"next-intl");let i=qt.join(e,c.VITEST_CONFIG);if(m(i)){let a=JSON.parse(await d(qt.join(e,c.PACKAGE_JSON))),p={...a.dependencies,...a.devDependencies},u=!!(p["@reduxjs/toolkit"]&&p["react-redux"]);await re(e,{redux:u,i18n:!0})}r.text="Formatting code...";let s=await O(e);await U(e,s,"lint:fix"),r.succeed(na.green("Internationalization setup successfully!"))}catch(o){throw r.fail("Failed to setup Internationalization."),o}finally{await S(t)}};var ni={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(),$e));return(await r(e)).hasI18n},files:[{path:"src/i18n",generated:!0,isDir:!0},{path:"src/proxy.ts",generated:!0},{path:"src/app/[locale]",generated:!0,isDir:!0}],packages:[{name:"next-intl",kind:"dependency"}],scripts:[],injections:[{file:"next.config.ts",description:"createNextIntlPlugin wrap in next.config.ts",presence:/next-intl\/plugin/},{file:"src/providers/RootProvider.tsx",description:"<NextIntlClientProvider> wrap",presence:/NextIntlClientProvider/}],apply:He};w();P();import ca from"node:path";w();import sa from"node:path";import aa from"picocolors";P();var Ue=async e=>{let r=R("Enabling React Compiler...");try{let t=sa.join(e,c.NEXT_CONFIG);if(!m(t))throw new Error(`${c.NEXT_CONFIG} not found.`);let o=await d(t);if(o.includes("reactCompiler:")){r.fail("React Compiler is already configured in next.config.ts.");return}let n=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/;if(!n.test(o))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");o=o.replace(n,`$1
|
|
768
|
+
reactCompiler: true,`),await f(t,o),r.text="Installing babel-plugin-react-compiler...",await rt(e,"babel-plugin-react-compiler"),r.succeed(aa.green("React Compiler enabled."))}catch(t){throw r.fail("Failed to enable React Compiler."),t}};var si={id:"react-compiler",name:"React Compiler",description:"reactCompiler: true in next.config.ts + babel-plugin-react-compiler",detect:async e=>{let r=ca.join(e,c.NEXT_CONFIG);return m(r)?/reactCompiler\s*:\s*true/.test(await d(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:Ue};w();import Qt from"node:path";import la from"picocolors";P();w();P();import ai from"node:fs/promises";import he from"node:path";import pa from"degit";var ci=async(e,r)=>{r.text="Fetching assets from starter repo...",await pa("teispace/nextjs-starter",{cache:!1,force:!0,verbose:!1}).clone(e)},pi=async(e,r)=>{let t=he.join(r,"src/providers/StoreProvider.tsx"),o=he.join(e,c.STORE_PROVIDER);await _(t,o);let n=he.join(r,"src/store"),i=he.join(e,c.STORE);await ai.cp(n,i,{recursive:!0})},li=async(e,r)=>{let t=he.join(r,"src/features/counter"),o=he.join(e,c.COUNTER_FEATURE);await ai.cp(t,o,{recursive:!0});let n=he.join(o,"components/Counter.tsx");if(await m(n)){let i=await d(n);i=i.replace(/import \{ useTranslations \} from 'next-intl';\n?/,""),i=i.replace(/const t = useTranslations\('Count'\);\n?/,""),i=i.replace(/\{t\('currentCount', \{ count: value \}\)\}/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(n,i)}};w();P();import Wt from"node:path";var mi=async e=>{let r=Wt.join(e,c.STORE),t=Wt.join(e,"package.json");if(m(r))return{isSetup:!0,reason:"src/store directory exists"};if(m(t)){let o=JSON.parse(await d(t));if(o.dependencies?.["@reduxjs/toolkit"]||o.devDependencies?.["@reduxjs/toolkit"])return{isSetup:!0,reason:"@reduxjs/toolkit is installed"}}return{isSetup:!1,reason:""}},di=async e=>{let r=Wt.join(e,c.PROVIDERS_INDEX);if(!await lt(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 Zt from"node:path";var ui=async e=>{},fi=async e=>{let r=Zt.join(e,"src/providers/RootProvider.tsx");if(!m(r))return;let t=await d(r),o="'use client';",n=t.includes(o);if(n&&(t=t.replace(o,"").trim()),t.includes("from '@/store'")||(t=`import type { AppState } from '@/store';
|
|
766
769
|
${t}`),t.includes("from './StoreProvider'")||(t=`${t.includes("from '@/store'"),""}import { StoreProvider } from './StoreProvider';
|
|
767
770
|
${t}`),n&&(t=`${o}
|
|
768
771
|
${t}`),t.includes("preloadedState")||(t=t.replace(/children:\s*React\.ReactNode;/,`children: React.ReactNode;
|
|
@@ -770,28 +773,261 @@ ${t}`),t.includes("preloadedState")||(t=t.replace(/children:\s*React\.ReactNode;
|
|
|
770
773
|
<StoreProvider preloadedState={preloadedState}>
|
|
771
774
|
${s}
|
|
772
775
|
</StoreProvider>
|
|
773
|
-
);`)}}await
|
|
776
|
+
);`)}}await f(r,t)},gi=async e=>{let r=[Zt.join(e,c.ROOT_PAGE),Zt.join(e,"src/app/[locale]/page.tsx")],t="";for(let n of r)if(m(n)){t=n;break}if(!t)return;let o=await d(t);if(o.includes("Counter")||(o=`import { Counter } from '@/features/counter';
|
|
774
777
|
${o}`),!o.includes("<Counter />")){let n=o.lastIndexOf("</div>"),i=o.lastIndexOf("</main>"),s=i!==-1?i:n;s!==-1&&(o=o.slice(0,s)+`
|
|
775
778
|
<div className="mt-8">
|
|
776
779
|
<h2 className="mb-4 text-2xl font-bold">Redux Counter</h2>
|
|
777
780
|
<Counter />
|
|
778
781
|
</div>
|
|
779
|
-
`+o.slice(s),await
|
|
782
|
+
`+o.slice(s),await f(t,o))}};var ze=async e=>{let r=R("Setting up Redux Toolkit..."),t=Qt.join(e,".next-maker-temp-redux");try{let{isSetup:o,reason:n}=await mi(e);if(o){r.fail(`Redux is already set up (${n}).`);return}await di(e),await ci(t,r),r.text="Copying Redux files...",await pi(e,t),r.text="Creating Counter feature...",await li(e,t),r.text="Updating providers and pages...",await ui(e),await fi(e),await gi(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 i=Qt.join(e,c.VITEST_CONFIG);if(m(i)){let a=JSON.parse(await d(Qt.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(la.green("Redux Toolkit setup successfully!"))}catch(o){throw r.fail("Failed to setup Redux Toolkit."),o}finally{await S(t)}};var hi={id:"redux",name:"Redux Toolkit",description:"Redux Toolkit + react-redux + redux-persist + StoreProvider",detect:async e=>{let{detectProjectSetup:r}=await Promise.resolve().then(()=>(L(),$e));return(await r(e)).hasRedux},files:[{path:"src/store",generated:!0,isDir:!0},{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/}],apply:ze};w();P();import ga from"node:path";w();import ua from"node:path";import fa from"picocolors";P();var yi=[{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"}],xi=/(const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{)/,ma=/^\s+headers\s*:/m,da=e=>`
|
|
783
|
+
headers: async () => {
|
|
784
|
+
return [
|
|
785
|
+
{
|
|
786
|
+
source: '/:path*',
|
|
787
|
+
headers: [
|
|
788
|
+
${e.map(({key:t,value:o})=>` { key: '${t}', value: '${o}' },`).join(`
|
|
789
|
+
`)}
|
|
790
|
+
],
|
|
791
|
+
},
|
|
792
|
+
];
|
|
793
|
+
},`,Se=e=>ma.test(e),er=(e,r=yi)=>{if(Se(e))return e;if(!xi.test(e))throw new Error("Could not locate `const nextConfig: NextConfig = {` in next.config.ts.");return e.replace(xi,`$1${da(r)}`)};var Ve=async e=>{let r=R("Adding security headers to next.config.ts...");try{let t=ua.join(e,c.NEXT_CONFIG);if(!m(t))throw new Error(`${c.NEXT_CONFIG} not found.`);let o=await d(t);if(Se(o)){r.succeed("Security headers already configured.");return}let n=er(o);await f(t,n),r.succeed(fa.green("Security headers added."))}catch(t){throw r.fail("Failed to add security headers."),t}};var Pi={id:"security-headers",name:"Security Headers",description:"Hardened HTTP headers in next.config.ts",detect:async e=>{let r=ga.join(e,c.NEXT_CONFIG);return m(r)?Se(await d(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:Ve};w();P();import va from"node:path";w();import ha from"node:fs/promises";import ae from"node:path";import xa from"degit";import ya from"picocolors";P();var Pa=["vitest","jsdom","@vitejs/plugin-react","@testing-library/dom","@testing-library/jest-dom","@testing-library/react","@testing-library/user-event"],Je=async e=>{let r=R("Setting up testing (Vitest + RTL)..."),t=ae.join(e,".next-maker-temp-tests");try{let o=ae.join(e,c.VITEST_CONFIG);if(m(o)){r.fail("Testing is already set up (vitest.config.ts exists).");return}r.text="Fetching assets from starter repo...",await xa("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 i=ae.join(e,c.TEST_DIR);await ha.mkdir(i,{recursive:!0}),await _(ae.join(t,c.TEST_DIR,"setup.ts"),ae.join(i,"setup.ts"));let s=ae.join(e,c.PACKAGE_JSON),a=JSON.parse(await d(s)),p={...a.dependencies,...a.devDependencies},u=!!(p["@reduxjs/toolkit"]&&p["react-redux"]),g=!!p["next-intl"];await re(e,{redux:u,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 x=await O(e);await ue(e,x,Pa),r.text="Formatting code...",await U(e,x,"lint:fix"),r.succeed(ya.green("Testing setup complete."))}catch(o){throw r.fail("Failed to set up testing."),o}finally{await S(t)}};var vi={id:"tests",name:"Tests",description:"Vitest + React Testing Library + jsdom",detect:async e=>m(va.join(e,c.VITEST_CONFIG)),files:[{path:c.VITEST_CONFIG,generated:!0},{path:"test",generated:!0,isDir:!0}],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:Je};P();import Ea from"node:path";w();import Si from"node:path";import Ca from"picocolors";P();var Sa={npm:"npm run",yarn:"yarn",pnpm:"pnpm",bun:"bun run"},wa=e=>{let r=Sa[e];return["ci:check","type-check","check:deprecated","test","build"].map(t=>`${r} ${t}`).join(" && ")},tr=(e,r)=>{let t={...e.scripts??{}},o={...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=wa(r)),!o.tsx&&!e.dependencies?.hasOwnProperty("tsx")&&(o.tsx="*"),{...e,scripts:t,devDependencies:o}};var rr=`import * as fs from 'node:fs';
|
|
794
|
+
import * as path from 'node:path';
|
|
795
|
+
|
|
796
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
797
|
+
const examplePath = path.join(process.cwd(), '.env.example');
|
|
798
|
+
|
|
799
|
+
if (!fs.existsSync(envPath)) {
|
|
800
|
+
console.log('\u26A0\uFE0F No .env file found. Skipping sync.');
|
|
801
|
+
process.exit(0);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const envContent = fs.readFileSync(envPath, 'utf-8');
|
|
805
|
+
const lines = envContent.split('\\n');
|
|
806
|
+
|
|
807
|
+
let isPublicBlock = false;
|
|
808
|
+
|
|
809
|
+
const exampleLines = lines.map((line) => {
|
|
810
|
+
const trimmed = line.trim();
|
|
811
|
+
|
|
812
|
+
// Preserve empty lines and reset public block state
|
|
813
|
+
if (!trimmed) {
|
|
814
|
+
isPublicBlock = false;
|
|
815
|
+
return line;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Preserve comments
|
|
819
|
+
if (trimmed.startsWith('#')) {
|
|
820
|
+
if (line.toLowerCase().includes('-public')) {
|
|
821
|
+
isPublicBlock = true;
|
|
822
|
+
}
|
|
823
|
+
return line;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Parse KEY=VALUE
|
|
827
|
+
const match = line.match(/^([^=]+)=(.*)$/);
|
|
828
|
+
|
|
829
|
+
if (match) {
|
|
830
|
+
const key = match[1];
|
|
831
|
+
|
|
832
|
+
// Public marker: explicit "# -public" comment, or "KEY=VAL # -public"
|
|
833
|
+
if (isPublicBlock || line.toLowerCase().includes('-public')) {
|
|
834
|
+
isPublicBlock = false;
|
|
835
|
+
return line;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
isPublicBlock = false;
|
|
839
|
+
return \`\${key}=\`;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
isPublicBlock = false;
|
|
843
|
+
return line;
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
const newExampleContent = exampleLines.join('\\n');
|
|
847
|
+
|
|
848
|
+
let currentExampleContent = '';
|
|
849
|
+
if (fs.existsSync(examplePath)) {
|
|
850
|
+
currentExampleContent = fs.readFileSync(examplePath, 'utf-8');
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (currentExampleContent !== newExampleContent) {
|
|
854
|
+
fs.writeFileSync(examplePath, newExampleContent);
|
|
855
|
+
console.log('\u2705 Synchronized .env.example with .env');
|
|
856
|
+
} else {
|
|
857
|
+
console.log('\u2728 .env.example is already up to date');
|
|
858
|
+
}
|
|
859
|
+
`,or=`/**
|
|
860
|
+
* Walks every identifier in the project and flags any whose resolved TypeScript
|
|
861
|
+
* symbol carries a \`@deprecated\` JSDoc tag. Catches deprecated API usage that
|
|
862
|
+
* \`tsc --noEmit\` does not surface (deprecations are "suggestion"-level, not
|
|
863
|
+
* errors) and that Biome does not yet detect.
|
|
864
|
+
*
|
|
865
|
+
* Exits 1 if any deprecated usage is found (so it can gate CI).
|
|
866
|
+
*/
|
|
867
|
+
import path from 'node:path';
|
|
868
|
+
import ts from 'typescript';
|
|
869
|
+
|
|
870
|
+
const cwd = process.cwd();
|
|
871
|
+
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, 'tsconfig.json');
|
|
872
|
+
|
|
873
|
+
if (!configPath) {
|
|
874
|
+
console.error('tsconfig.json not found');
|
|
875
|
+
process.exit(1);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const raw = ts.readConfigFile(configPath, ts.sys.readFile).config;
|
|
879
|
+
const parsed = ts.parseJsonConfigFileContent(raw, ts.sys, path.dirname(configPath));
|
|
880
|
+
|
|
881
|
+
const program = ts.createProgram({
|
|
882
|
+
rootNames: parsed.fileNames,
|
|
883
|
+
options: parsed.options,
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
const checker = program.getTypeChecker();
|
|
887
|
+
|
|
888
|
+
function isDeprecated(sym: ts.Symbol | undefined): boolean {
|
|
889
|
+
if (!sym) return false;
|
|
890
|
+
return sym.getJsDocTags(checker).some((tag) => tag.name === 'deprecated');
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
let hits = 0;
|
|
894
|
+
|
|
895
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
896
|
+
if (sourceFile.fileName.includes('node_modules')) continue;
|
|
897
|
+
if (!sourceFile.fileName.startsWith(cwd)) continue;
|
|
898
|
+
|
|
899
|
+
const visit = (node: ts.Node): void => {
|
|
900
|
+
if (ts.isIdentifier(node)) {
|
|
901
|
+
const sym = checker.getSymbolAtLocation(node);
|
|
902
|
+
if (isDeprecated(sym)) {
|
|
903
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
904
|
+
console.log(
|
|
905
|
+
\`\${path.relative(cwd, sourceFile.fileName)}:\${line + 1}:\${character + 1} \${node.text} (@deprecated)\`,
|
|
906
|
+
);
|
|
907
|
+
hits++;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
ts.forEachChild(node, visit);
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
visit(sourceFile);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (hits === 0) {
|
|
917
|
+
console.log('\u2713 No deprecated API usage found.');
|
|
918
|
+
process.exit(0);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
console.log(\`\\n\${hits} deprecated symbol use(s) found.\`);
|
|
922
|
+
process.exit(1);
|
|
923
|
+
`;var Ta=[{relative:"scripts/sync-env.ts",content:rr},{relative:"scripts/check-deprecated.ts",content:or}],Be=async e=>{let r=R("Adding validation scripts...");try{let t=!1;for(let{relative:i,content:s}of Ta){let a=Si.join(e,i);m(a)||(await f(a,s),t=!0)}let o=await O(e),n=!1;await k(Si.join(e,c.PACKAGE_JSON),i=>{let s=i.devDependencies?.tsx,a=tr(i,o);return n=!s&&!!a.devDependencies?.tsx,a}),n&&(r.text="Installing tsx...",await ue(e,o,["tsx"])),r.succeed(Ca.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 wi=["scripts/sync-env.ts","scripts/check-deprecated.ts"],Ci={id:"validate-scripts",name:"Validation Scripts",description:"sync-env, check-deprecated, and the validate chain",detect:async e=>wi.every(r=>m(Ea.join(e,r))),files:wi.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:Be};w();P();import we from"node:path";var Ti=async e=>{let r=we.join(e,c.PACKAGE_JSON);return m(r)?JSON.parse(await d(r)):null},Ra=async(e,r)=>{let t=we.join(e,c.PACKAGE_JSON);await f(t,`${JSON.stringify(r,null,2)}
|
|
924
|
+
`)},Ei=(e,r)=>(r==="dependency"?e.dependencies:e.devDependencies)??{},Xe=async(e,r)=>{let t=await e.detect(r),o=[];if(!t)return{manifest:e,installed:!1,drift:[]};for(let i of e.files)i.required!==!1&&(m(we.join(r,i.path))||o.push({kind:"missingFile",file:i.path}));let n=await Ti(r);if(n){for(let s of e.packages)Ei(n,s.kind)[s.name]||o.push({kind:"missingPackage",name:s.name,depKind:s.kind});let i=n.scripts??{};for(let s of e.scripts){let a=i[s.name];a?s.expectedValue&&a!==s.expectedValue&&o.push({kind:"mismatchedScript",name:s.name,expected:s.expectedValue,actual:a}):o.push({kind:"missingScript",name:s.name})}}for(let i of e.injections){let s=we.join(r,i.file);if(!m(s)){o.push({kind:"missingInjection",file:i.file,description:i.description});continue}let a=await d(s);i.presence.test(a)||o.push({kind:"missingInjection",file:i.file,description:i.description})}return{manifest:e,installed:t,drift:o}},gt=async(e,r,t={})=>{let o=t.dryRun??!1,n={filesRemoved:[],packagesUninstalled:[],scriptsRemoved:[],blocksStripped:[],manualCleanup:[]};for(let a of e.injections){let p=we.join(r,a.file);if(!m(p))continue;if(!a.removePattern){n.manualCleanup.push({file:a.file,description:a.description});continue}let u=await d(p);if(!a.presence.test(u))continue;let g=u.replace(a.removePattern,"");g!==u?(o||await f(p,g),n.blocksStripped.push({file:a.file,description:a.description})):n.manualCleanup.push({file:a.file,description:a.description})}let{rm:i}=await import("node:fs/promises");for(let a of e.files){if(!a.generated)continue;let p=we.join(r,a.path);m(p)&&(o||await i(p,{recursive:!0,force:!0}),n.filesRemoved.push(a.path))}let s=await Ti(r);if(s){let a=!1;if(s.scripts)for(let p of e.scripts)s.scripts[p.name]&&(o||delete s.scripts[p.name],n.scriptsRemoved.push(p.name),a=!0);for(let p of e.packages)Ei(s,p.kind)[p.name]&&n.packagesUninstalled.push(p.name);a&&!o&&await Ra(r,s)}if(!o&&n.packagesUninstalled.length>0){let a=await O(r);await St(r,a,n.packagesUninstalled)}return n};var xe=[Pi,Ci,Ao,si,No,vi,Uo,Ko,hi,ni],Ri=e=>xe.find(r=>r.id===e);var $a=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}`}},ba=e=>{let r=e.filter(o=>o.installed),t=r.filter(o=>o.drift.length>0);return{installedCount:r.length,driftedCount:t.length,cleanCount:r.length-t.length,driftedResults:t}},$i=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(),o=r.feature?xe.filter(s=>s.id===r.feature):xe;if(r.feature&&o.length===0)throw new Error(`Unknown feature "${r.feature}". Valid: ${xe.map(s=>s.id).join(", ")}.`);let n=[];for(let s of o)n.push(await Xe(s,t));r.json&&(l(JSON.stringify(n.map(s=>({id:s.manifest.id,name:s.manifest.name,installed:s.installed,drift:s.drift})),null,2)),process.exit(n.some(s=>s.drift.length>0)?1:0)),l(j.cyan(`
|
|
925
|
+
\u{1FA7A} Project Doctor
|
|
926
|
+
`));for(let s of n){let{manifest:a,installed:p,drift:u}=s;if(!p){l(` ${j.dim("\u2014")} ${j.dim(a.name)} ${j.dim("(not installed)")}`);continue}if(u.length===0){l(` ${j.green("\u2713")} ${a.name}`);continue}l(` ${j.yellow("!")} ${j.bold(a.name)} ${j.dim(`(${u.length} issue${u.length===1?"":"s"})`)}`);for(let g of u)l(` ${j.yellow("\u2022")} ${$a(g)}`)}let i=ba(n);l(""),l(j.dim(`${i.cleanCount} clean, ${i.driftedCount} drifted, ${n.length-i.installedCount} not installed`)),i.driftedCount===0&&(l(j.green(`
|
|
927
|
+
\u2713 No drift detected.
|
|
928
|
+
`)),process.exit(0)),r.fix||(l(j.dim(`
|
|
929
|
+
Run with --fix to re-apply drifted features.
|
|
930
|
+
`)),process.exit(1)),l(j.cyan(`
|
|
931
|
+
\u{1F527} Applying fixes...
|
|
932
|
+
`));for(let s of i.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
|
+
\u2713 Doctor finished.
|
|
934
|
+
`))}catch(t){v(`${t instanceof Error?t.message:t}`),process.exit(1)}})};import Ce from"picocolors";w();P();import ir from"node:path";var Na=/^[A-Z][A-Z0-9_]*$/,Ni=e=>{if(!Na.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,Ia=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,"\\'")}')`}},Aa=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(o=>`'${o.replace(/'/g,"\\'")}'`).join(", ")}])`}default:return"z.string()"}},Ii=e=>{let r=ce(e.name,e.public),o=Aa(e),n=e.default!==void 0,i=!e.required&&!n;n&&(o+=Ia(e)),i&&e.type!=="enum"&&(o+=".optional()");let s;if(e.type==="enum"?s=o:s=`z.preprocess(emptyStringToUndefined, ${o})`,e.description){let a=e.description.replace(/'/g,"\\'");s+=`
|
|
935
|
+
.describe('${a}')`}return` ${r}: ${s},`},bi=/(export const envSchema\s*=\s*z\.object\(\{)([\s\S]*?)(\}\);?)/,Ai=(e,r)=>{let t=ce(r.name,r.public);if(new RegExp(`\\b${t}\\s*:`).test(e))return e;let n=e.match(bi);if(!n)throw new Error("Could not locate `export const envSchema = z.object({ \u2026 })` in schema.ts.");let[,i,s,a]=n,p=s.replace(/\s+$/,""),u=p.length===0||/,\s*$/.test(p)?p:`${p},`,g=Ii(r),x=`${u}
|
|
936
|
+
${g}
|
|
937
|
+
`;return e.replace(bi,`${i}${x}${a}`)},ka="# -public",Oa=e=>{let r=ce(e.name,e.public),t=e.default??"",o=e.public?` ${ka}`:"";return`${r}=${t}${o}`},ki=(e,r)=>{let t=ce(r.name,r.public);if(new RegExp(`^${t}=`,"m").test(e))return e;let n=e===""||e.endsWith(`
|
|
938
|
+
`)?"":`
|
|
939
|
+
`,i=e===""?"":`
|
|
940
|
+
`,s=r.description?`# ${r.description}
|
|
941
|
+
`:"";return`${e}${n}${i}${s}${Oa(r)}
|
|
942
|
+
`},Oi=(e,r)=>{let t=ce(r.name,r.public);if(new RegExp(`^${t}=`,"m").test(e))return e;let n=e===""||e.endsWith(`
|
|
943
|
+
`)?"":`
|
|
944
|
+
`,i=r.default??"";return`${e}${n}${t}=${i}
|
|
945
|
+
`},nr=async(e,r)=>{Ni(r.name);let t=ir.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 o=await d(t),n=Ai(o,r),i=n!==o;i&&await f(t,n);let s=ir.join(e,c.ENV_EXAMPLE),a=!1;if(m(s)){let g=await d(s),x=ki(g,r);a=x!==g,a&&await f(s,x)}let p=ir.join(e,".env"),u=!1;if(m(p)){let g=await d(p),x=Oi(g,r);u=x!==g,u&&await f(p,x)}return{schemaUpdated:i,envExampleUpdated:a,envUpdated:u}};var _i=new Set(["string","url","number","boolean","enum"]),_a=e=>{let r=(e??"string").toLowerCase();if(!_i.has(r))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(_i).join(", ")}.`);return r},Fa=e=>{if(!e)return;let r=e.split(",").map(t=>t.trim()).filter(Boolean);return r.length===0?void 0:r},Fi=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 o=process.cwd();if(l(Ce.cyan(`
|
|
946
|
+
\u{1F510} Env Var Generator
|
|
947
|
+
`)),t.required&&t.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let n=_a(t.type),i=Fa(t.enum);if(n==="enum"&&(!i||i.length===0))throw new Error("--type=enum requires at least one value via --enum a,b,c.");let s={name:r,type:n,required:!!t.required,default:t.default,description:t.describe,enumValues:i,public:!!t.public};h.start("Updating schema, .env.example, and .env...");let a=await nr(o,s);h.succeed("Env var written");let p=ce(s.name,s.public);l(Ce.green(`
|
|
948
|
+
\u2728 ${p} declared.
|
|
949
|
+
`)),l(Ce.dim("Touched:")),l(Ce.dim(` ${a.schemaUpdated?"\u270F\uFE0F ":"\u23ED "} src/lib/env/schema.ts`)),l(Ce.dim(` ${a.envExampleUpdated?"\u270F\uFE0F ":"\u23ED "} .env.example`)),l(Ce.dim(` ${a.envUpdated?"\u270F\uFE0F ":"\u23ED "} .env`)),l("")}catch(o){h.fail("Env var declaration failed"),v(`${o instanceof Error?o.message:o}`),process.exit(1)}})};import ur from"node:path";import D from"picocolors";L();L();import Za from"node:path";import{readFile as ja,writeFile as Da}from"node:fs/promises";import La from"node:path";var Ye=(e,r)=>{let t=/import\s+.*\s+from\s+['"].*['"];?\n/g,o=e.match(t);if(o&&o.length>0){let n=o[o.length-1],i=e.lastIndexOf(n);return e.slice(0,i+n.length)+r+`
|
|
950
|
+
`+e.slice(i+n.length)}return`${r}
|
|
951
|
+
${e}`},sr=(e,r,t)=>{let o=/combineReducers\(\{([^}]*)\}\)/s,n=e.match(o);if(!n)throw new Error("Could not find combineReducers in rootReducer.ts");let i=n[1],s=`
|
|
952
|
+
${r}: ${t},`,a=i.trimEnd()+s;return e.replace(o,`combineReducers({${a}
|
|
953
|
+
})`)},Ke=(e,r)=>{let t=e.lastIndexOf("} as const;");if(t===-1)throw new Error("Could not find closing brace of AppApis object");let o=e.substring(0,t),n=e.substring(t);return`${o.trimEnd()}
|
|
954
|
+
${r}
|
|
955
|
+
${n}`};var qe=async e=>{let{serviceName:r,projectPath:t}=e,o=A(r),n=La.join(t,"src","lib","config","app-apis.ts");try{let i=await ja(n,"utf-8");if(i.includes(`${o}:`))return;if(!i.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let s=` ${o}: {
|
|
956
|
+
base: \`\${API_PREFIX}/${r}\`,
|
|
957
|
+
getAll: \`\${API_PREFIX}/${r}\`,
|
|
958
|
+
},`;i=Ke(i,s),await Da(n,i)}catch(i){throw new Error(`Failed to register API endpoints: ${i}`,{cause:i})}};import{readFile as Ma,writeFile as Ga}from"node:fs/promises";import Ha from"node:path";P();var ar=async(e,r,t)=>{let o=Ha.join(e,"src","lib","config","app-paths.ts");if(!m(o))return;let n=await Ma(o,"utf-8"),i=A(r);if(n.includes(`${i}:`))return;let s=n.lastIndexOf("} as const;");if(s===-1)return;let a=` ${i}: '${t}',
|
|
959
|
+
`;n=n.slice(0,s)+a+n.slice(s),await Ga(o,n)};w();P();import ht from"node:path";var Ua=e=>new RegExp(`<\\s*${e}\\b`),ji=(e,r)=>{if(Ua(r).test(e))return e;let t=/^(?<indent>[ \t]*)\{children\}\s*$/m,o=e.match(t);if(!o||o.index===void 0)throw new Error("Could not locate `{children}` line in RootProvider \u2014 expected a line containing only `{children}`.");let n=o.groups?.indent??"",i=`${n} `,s=`${n}<${r}>
|
|
960
|
+
${i}{children}
|
|
961
|
+
${n}</${r}>`;return e.replace(t,s)},Di=(e,r)=>{let t=`export * from './${r}';`;if(e.includes(t))return e;let o=e.split(`
|
|
962
|
+
`),n=[],i=[];for(let x of o)/^export \* from '\.\/.*';\s*$/.test(x)?n.push(x):i.push(x);let s=[...n,t].sort(),a=o.findIndex(x=>/^export \* from '\.\//.test(x));if(a===-1)return`${t}
|
|
963
|
+
${e.replace(/^\n+/,"")}`;let p=o.slice(0,a),u=o.findIndex((x,y)=>y>a&&!/^export \* from '\.\//.test(x)),g=u===-1?[]:o.slice(u);return[...p,...s,...g].join(`
|
|
964
|
+
`)},za=c.PROVIDERS,Va=c.ROOT_PROVIDER,Ja=c.PROVIDERS_INDEX,Li=async(e,r={fileExists:m,readFile:d})=>{let t=ht.join(e,Va);if(r.fileExists(t))return t;let o=ht.join(e,za);if(!r.fileExists(o))return null;let{readdir:n}=await import("node:fs/promises"),s=(await n(o).catch(()=>[])).filter(p=>p.endsWith(".tsx")),a=null;for(let p of s){let u=ht.join(o,p),g=await r.readFile(u);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:u,depth:y})}return a?.file??null},cr=async e=>{let{projectPath:r,componentName:t,fileBaseName:o}=e,n=await Li(r);if(n){let a=await d(n),p=a;(!/from '\.\//.test(a)||!a.includes(`{ ${t} }`))&&(p=Ye(p,`import { ${t} } from './${o}';`)),p=ji(p,t),p!==a&&await f(n,p)}let i=ht.join(r,Ja),s=null;if(m(i)){let a=await d(i),p=Di(a,o);p!==a&&await f(i,p),s=i}return{rootProviderFile:n,barrelFile:s}};import{readFile as Ba,writeFile as Xa}from"node:fs/promises";import Ya from"node:path";var We=async e=>{let{projectPath:r,name:t,persist:o,importPath:n}=e,i=Ya.join(r,"src","store","rootReducer.ts");try{let s=await Ba(i,"utf-8"),a=A(t),p=`${a}Reducer`,u=`${a}PersistConfig`,g=n.replace(/^src\//,"@/"),x=o?`import { ${p}, ${u} } from '${g}';`:`import { ${p} } from '${g}';`;s=Ye(s,x);let y=o?`persistReducer(${u}, ${p})`:p;s=sr(s,a,y),await Xa(i,s)}catch(s){throw new Error(`Failed to register reducer '${t}' in rootReducer: ${s}`,{cause:s})}};P();import{readFile as Ka,writeFile as qa}from"node:fs/promises";import Wa from"node:path";var pr=async(e,r)=>{let t=Wa.join(e,"src","i18n","translations","en.json");if(!m(t))return;let o=await Ka(t,"utf-8"),n=JSON.parse(o);n[r]||(n[r]={title:r,description:`${r} page description`},await qa(t,`${JSON.stringify(n,null,2)}
|
|
965
|
+
`))};var lr=()=>[{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 Dt({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 qe({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 We({projectPath:e.projectPath,name:e.featureName,persist:e.persistStore,importPath:Za.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(o){throw r.spinner.fail(t.name),o}}};var mr=()=>[{name:"Service files generated",spinnerText:"Generating service files...",execute:async e=>{await be({name:e.serviceName,outputPath:e.servicePath,httpClient:e.httpClient})}},{name:"API endpoints registered",spinnerText:"Registering API endpoints...",execute:async e=>{await qe({serviceName:e.serviceName,projectPath:e.projectPath})}}];import Qa from"node:path";var dr=()=>[{name:"Slice files generated",spinnerText:"Generating slice files...",execute:async e=>{await Ae({name:e.sliceName,outputPath:e.slicePath,persist:e.persistSlice})}},{name:"Slice registered in rootReducer",spinnerText:"Registering slice in rootReducer...",execute:async e=>{await We({projectPath:e.projectPath,name:e.sliceName,persist:e.persistSlice,importPath:Qa.join(e.basePath,e.sliceName)})}}];import ec from"enquirer";var{prompt:tc}=ec,Mi=async(e,r,t,o,n,i,s)=>{let a=[];e||a.push({type:"input",name:"featureName",message:"What is the feature name?",initial:"my-feature",validate:u=>/^[a-z0-9-]+$/.test(u)?!0:"Feature name must be lowercase and contain only alphanumeric characters and hyphens."}),r&&o===void 0&&n===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"&&i===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 tc(a):{};return{featureName:e||p.featureName,hasRedux:r||!1,createStore:o===!0?!1:n!==void 0?!0:p.createStore||!1,persistStore:n==="persist"?!0:n==="no-persist"?!1:p.persistStore||!1,httpClient:t||"none",createService:i===!0?!1:s!==void 0?!0:p.createService||!1,selectedHttpClient:s||p.selectedHttpClient||(t==="both"?"fetch":t!=="none"?t:void 0)}};var Gi=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 o=process.cwd();rc(t),l(D.cyan(`
|
|
966
|
+
\u{1F3AF} Feature Generator
|
|
967
|
+
`));let n=lr();h.start("Detecting project setup...");let{detectProjectSetup:i}=await Promise.resolve().then(()=>(L(),$e)),s=await i(o);h.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"}
|
|
968
|
+
`));let a=await Mi(r,s.hasRedux,s.httpClient,t.skipStore,t.store,t.skipService,t.service),p=t.path||ur.join("src","features"),u=ur.join(o,p,a.featureName);await ne(o,a.featureName,p)&&(v(`Feature '${a.featureName}' already exists at ${p}!`),process.exit(1)),await ye(n.slice(1),{projectPath:o,spinner:h,featureName:a.featureName,basePath:p,featurePath:u,createStore:a.createStore,persistStore:a.persistStore,createService:a.createService,httpClient:a.selectedHttpClient,detection:s}),oc(a,p)}catch(o){h.fail("Feature generation failed"),v(`${o}`),process.exit(1)}})},rc=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))},oc=(e,r)=>{let t=ur.join(r,e.featureName);l(D.green(`
|
|
969
|
+
\u2728 Feature '${e.featureName}' created successfully!
|
|
970
|
+
`)),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 o=r.replace(/^src\//,"@/");l(D.dim(` 1. Import and use the feature: import { ${e.featureName} } from '${o}/${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 fr from"node:path";import Te from"picocolors";L();import ic from"enquirer";var{prompt:nc}=ic,Hi=async e=>e?{hookName:e}:await nc({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 Ui=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 o=process.cwd();l(Te.cyan(`
|
|
972
|
+
\u{1FA9D} Hook Generator
|
|
973
|
+
`));let n=await I(o),i=await Hi(r),a=`use${$(i.hookName)}`,p=sc(t,n.hasTests);h.start("Generating hook..."),await Mt({name:i.hookName,projectPath:o,isClient:t.client??!0,featurePath:t.feature}),h.succeed("Hook generated");let u=t.feature?fr.join(o,t.feature,"hooks",`${a}.ts`):fr.join(o,"src/hooks",`${a}.ts`),g=null;p&&(h.start("Generating test..."),g=await Q({projectPath:o,sourceFile:u,kind:"hook",symbolName:a,hookUsesStore:n.hasRedux&&!!t.feature}),h.succeed("Test generated"));let x=t.feature?`${t.feature}/hooks/${a}.ts`:`src/hooks/${a}.ts`;l(Te.green(`
|
|
974
|
+
\u2728 Hook '${a}' created successfully!
|
|
975
|
+
`)),l(Te.dim(` \u{1F4C4} ${x}`)),g&&l(Te.dim(` \u{1F9EA} ${fr.relative(o,g)}`)),l(""),l(Te.cyan("Usage:"));let y=t.feature?`${t.feature.replace(/^src\//,"@/")}/hooks/${a}`:`@/hooks/${a}`;l(Te.dim(` import { ${a} } from '${y}';`)),l("")}catch(o){h.fail("Hook generation failed"),v(`${o}`),process.exit(1)}})},sc=(e,r)=>e.noTest?!1:e.test?!0:r;import xt from"picocolors";L();var zi=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 o=process.cwd();l(xt.cyan(`
|
|
976
|
+
\u{1F4D0} Layout Generator
|
|
977
|
+
`)),h.start("Detecting project setup...");let n=await I(o);h.succeed("Project setup detected");let i=n.hasI18n&&t.locale!==!1;l(xt.dim(` i18n: ${i?"\u2713":"\u2717"}
|
|
978
|
+
`)),h.start("Generating layout...");let s=await Gt({segment:r,at:t.at,projectPath:o,hasI18n:i,withGroup:!!t.group});h.succeed("Layout generated"),l(xt.green(`
|
|
979
|
+
\u2728 ${s.componentName} created!
|
|
980
|
+
`)),l(xt.dim(` \u{1F4C4} ${s.displayPath}`)),l("")}catch(o){h.fail("Layout generation failed"),v(`${o instanceof Error?o.message:o}`),process.exit(1)}})};import pc from"node:path";import W from"picocolors";P();L();import ac from"enquirer";var{prompt:cc}=ac,Vi=async e=>{let r=[];e||r.push({type:"input",name:"code",message:"Locale code (e.g., es, fr, de):",validate:o=>o?/^[a-z]{2}(-[A-Z]{2})?$/.test(o)?!0:"Use format: xx or xx-XX (e.g., es, pt-BR)":"Locale code is required"}),r.push({type:"input",name:"name",message:"Language name (e.g., Spanish, French):",validate:o=>o?!0:"Language name is required"},{type:"input",name:"country",message:"Country (e.g., Spain, France):",validate:o=>o?!0:"Country is required"},{type:"input",name:"flag",message:"Flag emoji (e.g., \u{1F1EA}\u{1F1F8}, \u{1F1EB}\u{1F1F7}):",validate:o=>o?!0:"Flag emoji is required"},{type:"confirm",name:"copyTranslations",message:"Copy translations from English? (No = empty values)",initial:!1});let t=await cc(r);return{code:e||t.code,name:t.name,country:t.country,flag:t.flag,copyTranslations:t.copyTranslations}};var Ji=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 o=process.cwd();l(W.cyan(`
|
|
981
|
+
\u{1F30D} Locale Generator
|
|
982
|
+
`)),h.start("Detecting project setup...");let n=await I(o);h.succeed("Project setup detected"),n.hasI18n||(h.fail("i18n is not set up in this project"),v("Please set up internationalization first"),l(W.dim(`
|
|
983
|
+
Run: npx @teispace/next-maker setup --i18n
|
|
984
|
+
`)),process.exit(1));let i=await Vi(r),s=pc.join(o,"src","i18n","translations",`${i.code}.json`);m(s)&&(v(`Locale '${i.code}' already exists!`),process.exit(1)),h.start("Adding locale..."),await Ht({...i,projectPath:o,copyTranslations:t.copyTranslations??i.copyTranslations}),h.succeed("Locale added"),l(W.green(`
|
|
985
|
+
\u2728 Locale '${i.code}' (${i.name}) added successfully!
|
|
986
|
+
`)),l(W.dim("Updated files:")),l(W.dim(` \u{1F4C4} src/i18n/translations/${i.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/${i.code}.json`)),l(W.dim(` 2. Visit: http://localhost:3000/${i.code}`)),l("")}catch(o){h.fail("Locale generation failed"),v(`${o}`),process.exit(1)}})};import Xi from"node:path";import J from"picocolors";L();import lc from"enquirer";var{prompt:mc}=lc,Bi=async e=>{let r=[];e||r.push({type:"input",name:"pageName",message:"Page name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Page name is required"}),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 mc(r);return{pageName:e||t.pageName,withLoading:t.withLoading,withError:t.withError}};var Yi=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 o=process.cwd();l(J.cyan(`
|
|
987
|
+
\u{1F4C4} Page Generator
|
|
988
|
+
`)),h.start("Detecting project setup...");let n=await I(o);h.succeed("Project setup detected"),l(J.dim(` i18n: ${n.hasI18n?"\u2713":"\u2717"}
|
|
989
|
+
`));let i=await Bi(r),s=i.pageName,a=$(s),p=n.hasI18n?"src/app/[locale]":"src/app";await ne(o,s,p)&&(v(`Page '${s}' already exists at ${p}/${s}!`),process.exit(1)),h.start("Generating page files..."),await zt({name:s,projectPath:o,hasI18n:n.hasI18n,dynamic:t.dynamic,withLoading:t.loading??i.withLoading,withError:t.error??i.withError}),h.succeed("Page files generated"),h.start("Registering route...");let g=t.dynamic?`/${s}/[${t.dynamic}]`:`/${s}`;await ar(o,s,g),h.succeed("Route registered"),n.hasI18n&&(h.start("Adding translation namespace..."),await pr(o,a),h.succeed("Translation namespace added"));let x=t.dynamic?Xi.join(p,s,`[${t.dynamic}]`):Xi.join(p,s);l(J.green(`
|
|
990
|
+
\u2728 Page '${s}' created successfully!
|
|
991
|
+
`)),l(J.dim("Generated files:")),l(J.dim(` \u{1F4C2} ${x}/`)),l(J.dim(" \u251C\u2500\u2500 page.tsx")),(t.loading??i.withLoading)&&l(J.dim(" \u251C\u2500\u2500 loading.tsx")),(t.error??i.withError)&&l(J.dim(" \u2514\u2500\u2500 error.tsx")),l(""),n.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(o){h.fail("Page generation failed"),v(`${o}`),process.exit(1)}})};import gr from"node:path";import pe from"picocolors";var Ki=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
|
+
\u{1F50C} Provider Generator
|
|
993
|
+
`)),h.start("Generating provider...");let{componentName:o,fileBaseName:n,providerFile:i}=await Jt({name:r,projectPath:t});h.succeed("Provider generated"),h.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:s,barrelFile:a}=await cr({projectPath:t,componentName:o,fileBaseName:n});h.succeed("Provider registered"),l(pe.green(`
|
|
994
|
+
\u2728 ${o} created!
|
|
995
|
+
`)),l(pe.dim("Generated files:")),l(pe.dim(` \u{1F4C4} ${gr.relative(t,i)}`)),s?l(pe.dim(` \u270F\uFE0F ${gr.relative(t,s)} \u2014 wrapped {children}`)):(l(pe.yellow(`
|
|
996
|
+
\u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),l(pe.dim(` <${o}>{children}</${o}>`))),a&&l(pe.dim(` \u270F\uFE0F ${gr.relative(t,a)} \u2014 added re-export`)),l("")}catch(t){h.fail("Provider generation failed"),v(`${t instanceof Error?t.message:t}`),process.exit(1)}})};import dc from"enquirer";import M from"picocolors";var{prompt:uc}=dc,fc=e=>{let r=[];for(let t of e.filesRemoved)r.push(` ${M.red("-")} delete file: ${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},gc=e=>e.filesRemoved.length===0&&e.blocksStripped.length===0&&e.scriptsRemoved.length===0&&e.packagesUninstalled.length===0&&e.manualCleanup.length===0,qi=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 o=process.cwd(),n=Ri(r);if(!n)throw new Error(`Unknown feature "${r}". Valid: ${xe.map(p=>p.id).join(", ")}.`);if(l(M.cyan(`
|
|
997
|
+
\u{1F5D1} Remove ${n.name}
|
|
998
|
+
`)),!(await Xe(n,o)).installed){l(M.dim(`${n.name} is not installed \u2014 nothing to remove.`));return}h.start("Computing changes...");let s=await gt(n,o,{dryRun:!0});if(h.succeed("Computed plan"),gc(s)){l(M.yellow(`
|
|
999
|
+
\u26A0\uFE0F Manifest had no removable artifacts. Nothing to do.
|
|
1000
|
+
`));return}l(M.bold(`
|
|
1001
|
+
Planned changes:
|
|
1002
|
+
`));for(let p of fc(s))l(p);if(l(""),t.dryRun){l(M.dim(`--dry-run set \u2014 exiting without writing.
|
|
1003
|
+
`));return}if(!t.yes){let{confirm:p}=await uc({type:"confirm",name:"confirm",message:`Apply the changes above to remove ${n.name}?`,initial:!1});if(!p){l(M.yellow(`
|
|
1004
|
+
Aborted.
|
|
1005
|
+
`));return}}h.start(`Removing ${n.name}...`);let a=n.remove?(await n.remove(o),s):await gt(n,o);if(h.succeed(`${n.name} removed`),l(M.green(`
|
|
1006
|
+
\u2713 ${n.name} removed.`)),a.manualCleanup.length>0){l(M.yellow(`
|
|
1007
|
+
\u26A0\uFE0F Manual cleanup still required:`));for(let p of a.manualCleanup)l(` - ${p.description} (${p.file})`)}l("")}catch(o){h.fail("Remove failed"),v(`${o instanceof Error?o.message:o}`),process.exit(1)}})};import{existsSync as Pc}from"node:fs";import{mkdir as vc}from"node:fs/promises";import Ze from"node:path";import B from"picocolors";L();import{readFile as hc,writeFile as xc}from"node:fs/promises";import yc from"node:path";var Wi=async(e,r)=>{let t=A(r),o=yc.join(e,"src","lib","config","app-apis.ts");try{let n=await hc(o,"utf-8");if(n.includes(`${t}:`))return;if(!n.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");let i=ro({camelName:t,kebabName:r});n=Ke(n,i),await xc(o,n)}catch(n){throw new Error(`Failed to register CRUD API endpoints: ${n}`,{cause:n})}};import Zi from"enquirer";var Qi=async(e,r,t,o)=>{let n=e||(await Zi.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,i;return r!==void 0?i="axios":t!==void 0?i="fetch":o==="axios"?i="axios":o==="fetch"?i="fetch":o==="both"?i=(await Zi.prompt({type:"select",name:"httpClient",message:"Choose HTTP client:",choices:["axios","fetch"]})).httpClient:i="axios",{serviceName:n,httpClient:i}};var en=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 o=process.cwd();t.axios&&t.fetch&&(v("Cannot use --axios and --fetch together"),process.exit(1)),l(B.cyan(`
|
|
1008
|
+
\u{1F527} Service Generator
|
|
1009
|
+
`)),h.start("Detecting project setup...");let n=await I(o);h.succeed("Project setup detected"),n.httpClient==="none"&&(h.fail("No HTTP client is setup in this project"),v("Please setup either AxiosClient or FetchClient first"),l(B.dim(`
|
|
1010
|
+
Check: lib/utils/http/axios-client or lib/utils/http/fetch-client
|
|
1011
|
+
`)),process.exit(1));let i=[];(n.httpClient==="axios"||n.httpClient==="both")&&i.push("Axios \u2713"),(n.httpClient==="fetch"||n.httpClient==="both")&&i.push("Fetch \u2713"),l(B.dim(` HTTP Clients: ${i.join(", ")}
|
|
1012
|
+
`));let s=await Qi(r,t.axios,t.fetch,n.httpClient),{basePath:a,servicePath:p}=Sc(o,s.serviceName,t.path);Pc(p)||await vc(p,{recursive:!0}),nt(o,a,`${s.serviceName}.service.ts`)&&(v(`Service '${s.serviceName}' already exists at ${a}!`),process.exit(1)),t.crud?(h.start("Generating CRUD service files..."),await jt({name:s.serviceName,outputPath:p,httpClient:s.httpClient}),h.succeed("CRUD service files generated"),h.start("Registering CRUD API endpoints..."),await Wi(o,s.serviceName),h.succeed("CRUD API endpoints registered")):await ye(mr(),{projectPath:o,spinner:h,serviceName:s.serviceName,servicePath:p,httpClient:s.httpClient}),wc(s,a,!!t.crud)}catch(o){h.fail("Service generation failed"),v(`${o}`),process.exit(1)}})},Sc=(e,r,t)=>{let o;if(t){let n=t.replace(/^src\//,"");if(n.startsWith("features/")){let i=n.split("/")[1];o=Ze.join("src","features",i,"services")}else o=Ze.join("src",n)}else o=Ze.join("src","features",r,"services");return{basePath:o,servicePath:Ze.join(e,o)}},wc=(e,r,t=!1)=>{let o=Ze.join(r,`${e.serviceName}.service.ts`);l(B.green(`
|
|
1013
|
+
\u2728 ${t?"CRUD ":""}Service '${e.serviceName}' created successfully!
|
|
1014
|
+
`)),l(B.dim("Generated files:")),l(B.dim(` \u{1F4C4} ${o}
|
|
1015
|
+
`)),l(B.cyan("Next steps:"));let n=r.replace(/^src\//,"@/");l(B.dim(` 1. Import service: import { ${e.serviceName}Service } from '${n}/${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 Cc from"enquirer";import hr from"picocolors";var{prompt:Tc}=Cc,tn=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(hr.cyan(`
|
|
780
1016
|
\u{1F527} Setup Wizard
|
|
781
|
-
`)),r.httpClient||r.darkTheme||r.redux||r.i18n||r.tests||r.reactCompiler||r.bundleAnalyzer){r.httpClient&&await
|
|
782
|
-
\u26A0\uFE0F ${n} setup is not implemented yet.`))}catch(t){h.fail("Setup failed"),
|
|
1017
|
+
`)),r.httpClient||r.darkTheme||r.redux||r.i18n||r.tests||r.reactCompiler||r.bundleAnalyzer||r.securityHeaders||r.validateScripts||r.commitizen){r.httpClient&&await Ge(process.cwd()),r.darkTheme&&await Le(process.cwd()),r.redux&&await ze(process.cwd()),r.i18n&&await He(process.cwd()),r.tests&&await Je(process.cwd()),r.reactCompiler&&await Ue(process.cwd()),r.bundleAnalyzer&&await Fe(process.cwd()),r.securityHeaders&&await Ve(process.cwd()),r.validateScripts&&await Be(process.cwd()),r.commitizen&&await je(process.cwd());return}let n=(await Tc([{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(n==="Cancel"){l(hr.yellow("Setup cancelled."));return}let s={"dark-theme":Le,redux:ze,"http-client":Ge,i18n:He,tests:Je,"react-compiler":Ue,"bundle-analyzer":Fe,"security-headers":Ve,"validate-scripts":Be,commitizen:je}[n];s?await s(process.cwd()):l(hr.yellow(`
|
|
1018
|
+
\u26A0\uFE0F ${n} setup is not implemented yet.`))}catch(t){h.fail("Setup failed"),v(`${t}`),process.exit(1)}})};import{existsSync as $c}from"node:fs";import{mkdir as bc}from"node:fs/promises";import le from"node:path";import G from"picocolors";L();import Ec from"enquirer";var{prompt:Rc}=Ec,rn=async(e,r,t)=>{let o=[];e||o.push({type:"input",name:"sliceName",message:"What is the slice name?",initial:"my-slice",validate:i=>/^[a-z0-9-]+$/.test(i)?!0:"Slice name must be lowercase and contain only alphanumeric characters and hyphens."}),r===void 0&&t===void 0&&o.push({type:"confirm",name:"persistSlice",message:"Enable persistence for this slice?",initial:!1});let n=o.length>0?await Rc(o):{};return{sliceName:e||n.sliceName,persistSlice:r===!0?!0:t===!1?!1:n.persistSlice||!1}};var on=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 o=process.cwd();t.persist&&t.noPersist===!0&&(v("Cannot use --persist and --no-persist together"),process.exit(1)),l(G.cyan(`
|
|
783
1019
|
\u{1F527} Slice Generator
|
|
784
|
-
`)),h.start("Detecting project setup...");let n=await
|
|
1020
|
+
`)),h.start("Detecting project setup...");let n=await I(o);h.succeed("Project setup detected"),n.hasRedux||(h.fail("Redux is not setup in this project"),v("Please install @reduxjs/toolkit and react-redux first"),l(G.dim(`
|
|
785
1021
|
Run: npm install @reduxjs/toolkit react-redux
|
|
786
|
-
`)),process.exit(1)),
|
|
787
|
-
`));let i=await
|
|
1022
|
+
`)),process.exit(1)),l(G.dim(` Redux: \u2713
|
|
1023
|
+
`));let i=await rn(r,t.persist,t.noPersist===!0?!1:void 0),{basePath:s,slicePath:a}=Nc(o,i.sliceName,t.path),p=le.join(o,s);$c(p)||await bc(p,{recursive:!0}),await ne(o,i.sliceName,s)&&(v(`Slice '${i.sliceName}' already exists at ${s}!`),process.exit(1)),await ye(dr(),{projectPath:o,spinner:h,sliceName:i.sliceName,basePath:s,slicePath:a,persistSlice:i.persistSlice});let g=null;if(Ic(t,n.hasTests)){let y=le.join(a,`${i.sliceName}.slice.ts`);h.start("Generating test..."),g=await Q({projectPath:o,sourceFile:y,kind:"slice",symbolName:A(i.sliceName)}),h.succeed("Test generated")}Ac(i,s,g,o)}catch(o){h.fail("Slice generation failed"),v(`${o}`),process.exit(1)}})},Nc=(e,r,t)=>{let o;if(t){let n=t.replace(/^src\//,"");if(n.startsWith("features/")){let i=n.split("/")[1];o=le.join("src","features",i,"store")}else o=le.join("src",n)}else o=le.join("src","features",r,"store");return{basePath:o,slicePath:le.join(e,o,r)}},Ic=(e,r)=>e.noTest?!1:e.test?!0:r,Ac=(e,r,t,o)=>{let n=le.join(r,e.sliceName);l(G.green(`
|
|
788
1024
|
\u2728 Slice '${e.sliceName}' created successfully!
|
|
789
|
-
`)),
|
|
790
|
-
`)),t&&
|
|
791
|
-
`)),
|
|
792
|
-
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 o=process.cwd(),n=
|
|
1025
|
+
`)),l(G.dim("Generated files:")),l(G.dim(` \u{1F4C2} ${n}/`)),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
|
|
1026
|
+
`)),t&&l(G.dim(` \u{1F9EA} ${le.relative(o,t)}
|
|
1027
|
+
`)),l(G.cyan("Next steps:"));let i=r.replace(/^src\//,"@/");l(G.dim(` 1. Import actions: import { setLoading, setError } from '${i}/${e.sliceName}'`)),l(G.dim(" 2. Use in component: dispatch(setLoading(true))")),l("")};import X from"node:path";import xr from"picocolors";P();L();var nn=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
|
|
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 o=process.cwd(),n=X.isAbsolute(r)?r:X.join(o,r);l(xr.cyan(`
|
|
793
1029
|
\u{1F9EA} Test Generator
|
|
794
|
-
`)),
|
|
795
|
-
\u2728 Test '${
|
|
796
|
-
`)),
|
|
1030
|
+
`)),m(n)||(v(`Source file not found: ${r}`),process.exit(1));let i=await I(o);i.hasTests||(v("Testing is not installed in this project. Run `next-maker setup --tests` first."),process.exit(1));let s=await d(n),a=kc(t.kind)??Oc(n,s);a||(v(`Could not infer test kind for ${X.basename(n)}. Pass --kind component|hook|slice.`),process.exit(1));let p=_c(n,a),u=Fc(n,a);m(u)&&!t.force&&(v(`Test file already exists: ${X.relative(o,u)}. Use --force to overwrite.`),process.exit(1)),h.start(`Generating ${a} test...`);let g=await Q({projectPath:o,sourceFile:n,kind:a,symbolName:p,hasRedux:i.hasRedux,hasI18n:i.hasI18n,hookUsesStore:a==="hook"&&i.hasRedux&&s.includes("useAppSelector")});h.succeed("Test generated"),l(xr.green(`
|
|
1031
|
+
\u2728 Test '${X.basename(g)}' created.
|
|
1032
|
+
`)),l(xr.dim(` \u{1F9EA} ${X.relative(o,g)}`)),l("")}catch(o){h.fail("Test generation failed"),v(`${o}`),process.exit(1)}})},kc=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},Oc=(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},_c=(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)},Fc=(e,r)=>{let t=X.dirname(e),o=X.basename(e).replace(/\.(tsx?|jsx?)$/i,""),n=r==="component"?".tsx":".ts";return X.join(t,`${o}.test${n}`)};var sn=e=>{Kr(e),tn(e),Gi(e),on(e),en(e),Yi(e),zi(e),$o(e),Ji(e),Ui(e),Ki(e),Fi(e),$i(e),qi(e),nn(e)};var Dc=process.emitWarning;process.emitWarning=function(e,r,t,o){if(!(typeof e=="string"&&e.includes("--localstorage-file")&&e.includes("was provided without a valid path")))return Dc.apply(process,arguments)};async function Lc(){Pr({logger:r=>tt(r)});let e=new jc;e.name("next-maker").description("Teispace Next.js Project Generator").version("1.0.0"),sn(e),e.parse()}Lc().catch(e=>{let r=e&&typeof e=="object"&&"message"in e?e.message:String(e);tt(`Unexpected error: ${r}`)});
|
|
797
1033
|
//# sourceMappingURL=index.js.map
|