create-next-pro-stack 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jahirul
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # 🚀 BlitzStack CLI Tool
2
+
3
+ **BlitzStack** is a powerful, professional-grade CLI tool designed to scaffold high-performance Next.js applications in seconds. It comes pre-configured with industry-standard patterns, a premium UI/UX structure, and a robust state-management architecture.
4
+
5
+ Built by **[Jahirul](https://github.com/jahirul077)** for developers who value speed, consistency, and best practices.
6
+
7
+ ---
8
+
9
+ ## 🌟 Why BlitzStack?
10
+
11
+ Staring at a blank `create-next-app` is time-consuming. **BlitzStack** bridges the gap between a raw installation and a production-ready application.
12
+
13
+ - **Zero-Config Professional Routing:** Automatically sets up Route Groups (`(main)`, `auth`, `dashboard`).
14
+ - **Premium UI Starter:** Includes a stunning Landing Page, Sidebar, and TopNavbar out of the box.
15
+ - **Dynamic Secure Storage:** Automatically generates unique LocalStorage keys for each project to avoid token conflicts.
16
+ - **Pre-baked Interceptors:** Axios is pre-configured with request/response interceptors for seamless API handling.
17
+ - **Production Ready:** Pre-configured `.env`, `vercel.json` (rewrites), and `.gitignore`.
18
+
19
+ ---
20
+
21
+ ## 📦 Core Tech Stack & Packages
22
+
23
+ BlitzStack automatically installs and configures the following professional packages:
24
+
25
+ | Category | Packages |
26
+ | :--------------------- | :-------------------------------------------- |
27
+ | **Framework** | Next.js (Latest), React 19 |
28
+ | **Styling** | Tailwind CSS v4, Lucide React, Shadcn UI |
29
+ | **State Management** | Redux Toolkit, React Redux |
30
+ | **Data Fetching** | @tanstack/react-query, Axios |
31
+ | **Forms & Validation** | React Hook Form |
32
+ | **UI Components** | Shadcn UI (Button), Sonner (Toasts), Recharts |
33
+ | **Utilities** | clsx, tailwind-merge, localforage |
34
+
35
+ ---
36
+
37
+ ## 🚀 Getting Started
38
+
39
+ ### 1. Local Installation
40
+
41
+ First, clone this repository and link the CLI tool to your local machine:
42
+
43
+ ```bash
44
+ # Clone the repository
45
+ git clone <your-repo-link>
46
+
47
+ # Navigate to the folder
48
+ cd create-next-stack-CLI-Tools
49
+
50
+ # Link the tool globally
51
+ npm link
52
+ ```
53
+
54
+ ### 2. Creating a New Project
55
+
56
+ Now you can create a new **BlitzStack** project from anywhere in your terminal:
57
+
58
+ ```bash
59
+ create-next-pro-stack my-awesome-project
60
+ ```
61
+
62
+ ### 3. Development
63
+
64
+ The tool will automatically:
65
+
66
+ - Initialize the Next.js app.
67
+ - Install all dependencies.
68
+ - Apply the professional folder structure.
69
+ - **Run the development server (`npm run dev`) automatically.**
70
+
71
+ ---
72
+
73
+ ## 📁 Folder Structure Overview
74
+
75
+ ```text
76
+ src/
77
+ ├── app/
78
+ │ ├── (main)/ # Landing & Public pages
79
+ │ ├── auth/ # Login/Register flows
80
+ │ └── dashboard/ # Protected dashboard routes
81
+ ├── components/ # Reusable UI components
82
+ ├── hooks/
83
+ │ └── Axios/ # Pre-configured useAxiosPublic & useAxiosPrivate
84
+ ├── providers/ # Redux, React Query, and UI Providers
85
+ ├── redux/ # Store configuration and Slices
86
+ ├── shared/ # Shared Layout components (Sidebar, Navbar)
87
+ └── utils/ # Helper functions (cn, localStorage, etc.)
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 🛠️ Key Features Under the Hood
93
+
94
+ ### Professional Axios Interceptors
95
+
96
+ Your project comes with two main API hooks:
97
+
98
+ - `useAxiosPublic`: For open APIs with built-in network error handling.
99
+ - `useAxiosPrivate`: For secure APIs. It automatically handles:
100
+ - Dynamic Authorization tokens.
101
+ - Automatic logout on 401 (Session Expired).
102
+ - Friendly `sonner` toasts for every error status (403, 404, 500+).
103
+
104
+ ### Vercel Ready
105
+
106
+ Includes a `vercel.json` with a catch-all rewrite rule:
107
+
108
+ ```json
109
+ {
110
+ "rewrites": [{ "source": "/(.*)", "destination": "/" }]
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## 🤝 Contributing
117
+
118
+ Contributions, issues, and feature requests are welcome! Feel free to check the [issues page](https://github.com/jahirul077).
119
+
120
+ ## 📝 License
121
+
122
+ Copyright © 2026 [Jahirul Islam](https://github.com/jahirul077).
123
+ This project is [MIT](https://opensource.org/licenses/MIT) licensed.
package/index.js ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from "child_process";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { fileURLToPath } from "url";
7
+ import readline from "readline";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ const run = (cmd) => execSync(cmd, { stdio: "inherit", shell: true });
13
+
14
+ function askQuestion(query) {
15
+ const rl = readline.createInterface({
16
+ input: process.stdin,
17
+ output: process.stdout,
18
+ });
19
+ return new Promise((resolve) =>
20
+ rl.question(query, (answer) => {
21
+ rl.close();
22
+ resolve(answer);
23
+ }),
24
+ );
25
+ }
26
+
27
+ const projectName = process.argv[2];
28
+
29
+ if (!projectName) {
30
+ console.log("❌ Please provide a project name. Example:");
31
+ console.log(" create-next-pro-stack my-next-app");
32
+ process.exit(1);
33
+ }
34
+
35
+ async function main() {
36
+ try {
37
+ if (fs.existsSync(projectName)) {
38
+ console.log(`\n⚠️ Folder "${projectName}" already exists!`);
39
+ process.exit(1);
40
+ }
41
+
42
+ console.log("\n🚀 Starting Next.js Stack Setup...\n");
43
+
44
+ const useTypeScript = await askQuestion(
45
+ "Do you want to use TypeScript? (y/n): ",
46
+ );
47
+ const isTypeScript =
48
+ useTypeScript.toLowerCase() === "y" ||
49
+ useTypeScript.toLowerCase() === "yes";
50
+
51
+ // 1. Create Next.js using official create-next-app (Latest)
52
+ console.log("\n📦 Initializing Next.js project...");
53
+ const tsFlag = isTypeScript ? "--ts" : "--js";
54
+ const craCommand = `npx create-next-app@latest ${projectName} ${tsFlag} --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm --no-git`;
55
+
56
+ run(craCommand);
57
+
58
+ // Explicitly remove .git folder if create-next-app initialized it
59
+ const gitDir = path.join(projectName, ".git");
60
+ if (fs.existsSync(gitDir)) {
61
+ fs.rmSync(gitDir, { recursive: true, force: true });
62
+ }
63
+
64
+ console.log("\n✅ Base Next.js created (and .git removed)!");
65
+
66
+ // 2. Install Core Stack Packages (Latest versions)
67
+ console.log("\nInstalling core stack packages...");
68
+
69
+ const dependencies = [
70
+ "axios",
71
+ "@reduxjs/toolkit",
72
+ "react-redux",
73
+ "@tanstack/react-query",
74
+ "@tanstack/react-query-devtools",
75
+ "@tanstack/react-table",
76
+ "react-hook-form",
77
+ "react-icons",
78
+ "react-hot-toast",
79
+ "sonner",
80
+ "recharts",
81
+ "lucide-react",
82
+ "react-router-dom",
83
+ "localforage",
84
+ "match-sorter",
85
+ "sort-by",
86
+ "clsx",
87
+ "tailwind-merge",
88
+ "tailwindcss",
89
+ "@tailwindcss/postcss",
90
+ ];
91
+
92
+ console.log("\nInstalling dependencies...");
93
+ run(`cd ${projectName} && npm install ${dependencies.join(" ")}`);
94
+
95
+ // 2.5 Initialize Shadcn UI (Default/Non-interactive)
96
+ console.log("\n🎨 Initializing Shadcn UI...");
97
+ run(`cd ${projectName} && npx shadcn@latest init -d`);
98
+
99
+ // Install common shadcn components
100
+ console.log("\n🧩 Installing Shadcn components (Button)...");
101
+ run(`cd ${projectName} && npx shadcn@latest add button -y`);
102
+
103
+ // 3. Run Custom Templates (Folder structure, setups)
104
+ console.log("\n📁 Applying custom folder structure and templates...");
105
+
106
+ const templatesDir = path.join(__dirname, "templates");
107
+ const setupModules = [
108
+ "folder-structure.js",
109
+ "utils-setup.js",
110
+ "dashboard-setup.js",
111
+ "axios-setup.js",
112
+ "redux-setup.js",
113
+ "react-query-setup.js",
114
+ "providers-setup.js",
115
+ "page-files.js",
116
+ "gitignore-setup.js",
117
+ "vercel-setup.js",
118
+ "env-setup.js",
119
+ "cleanup.js",
120
+ ];
121
+
122
+ for (const moduleName of setupModules) {
123
+ const modulePath = path.join(templatesDir, moduleName);
124
+ if (fs.existsSync(modulePath)) {
125
+ const module = await import(`file://${modulePath}`);
126
+ if (module.default) {
127
+ await module.default(projectName, isTypeScript);
128
+ }
129
+ }
130
+ }
131
+
132
+ console.log("\n✨ Setup complete! Starting development server... 🚀\n");
133
+ run(`cd ${projectName} && npm run dev`);
134
+ } catch (err) {
135
+ console.error("\n❌ Something went wrong:", err.message);
136
+ process.exit(1);
137
+ }
138
+ }
139
+
140
+ main();
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "create-next-pro-stack",
3
+ "version": "1.0.0",
4
+ "description": "A professional CLI tool to quickly scaffold Next.js projects with Redux, React Query, TailwindCSS, and premium UI.",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "create-next-pro-stack": "index.js"
9
+ },
10
+ "keywords": [
11
+ "nextjs",
12
+ "cli",
13
+ "scaffold",
14
+ "redux",
15
+ "react-query",
16
+ "tailwindcss",
17
+ "shadcn"
18
+ ],
19
+ "author": "Jahirul Islam",
20
+ "license": "MIT",
21
+ "dependencies": {}
22
+ }
@@ -0,0 +1,166 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "tsx" : "jsx";
6
+ const axiosDir = path.join(projectName, "src", "hooks", "Axios");
7
+ if (!fs.existsSync(axiosDir)) fs.mkdirSync(axiosDir, { recursive: true });
8
+
9
+ const tokenKey = `${projectName.toUpperCase().replace(/[-\s]/g, "_")}_USER_ACCESS_TOKEN`;
10
+
11
+ // 1. Axios Public Logic (JSX/TSX Mode)
12
+ const publicHookContent = `import axios from "axios";
13
+
14
+ const BASE_URL = \`\${process.env.NEXT_PUBLIC_BASE_URL}/api\`;
15
+
16
+ if (!process.env.NEXT_PUBLIC_BASE_URL) {
17
+ console.warn("NEXT_PUBLIC_BASE_URL is not defined in .env file");
18
+ }
19
+
20
+ const axiosInstance = axios.create({
21
+ baseURL: BASE_URL,
22
+ timeout: 10000,
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ Accept: "application/json",
26
+ },
27
+ withCredentials: false,
28
+ });
29
+
30
+ export const useAxiosPublic = () => axiosInstance;
31
+
32
+ axiosInstance.interceptors.response.use(
33
+ (response) => response,
34
+ (error) => {
35
+ if (!error.response) {
36
+ return Promise.reject({
37
+ message: "Network error. Please check your connection.",
38
+ status: 0,
39
+ });
40
+ }
41
+
42
+ const { status, data } = error.response;
43
+
44
+ return Promise.reject({
45
+ status,
46
+ message: data?.message || "Something went wrong. Please try again.",
47
+ ...data,
48
+ });
49
+ }
50
+ );
51
+
52
+ export default useAxiosPublic;
53
+ `;
54
+
55
+ // 2. Axios Private Logic (JSX/TSX Mode)
56
+ const privateHookContent = `import { getLocalStorage, removeLocalStorage } from "@/utils/localStorage";
57
+ import {
58
+ getSessionStorage,
59
+ removeSessionStorage,
60
+ } from "@/utils/sessionStorage";
61
+ import axios from "axios";
62
+ import { toast } from "sonner";
63
+
64
+ const axiosInstance = axios.create({
65
+ baseURL: \`\${process.env.NEXT_PUBLIC_BASE_URL}/api\`,
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ Accept: "application/json",
69
+ },
70
+ timeout: 30000,
71
+ });
72
+
73
+ const useAxiosPrivate = () => axiosInstance;
74
+
75
+ axiosInstance.interceptors.request.use(
76
+ (config) => {
77
+ const token =
78
+ getLocalStorage("${tokenKey}") ||
79
+ getSessionStorage("${tokenKey}");
80
+
81
+ if (token) {
82
+ config.headers.Authorization = \`Bearer \${token}\`;
83
+ }
84
+
85
+ return config;
86
+ },
87
+ (error) => Promise.reject(error)
88
+ );
89
+
90
+ // RESPONSE INTERCEPTOR
91
+ axiosInstance.interceptors.response.use(
92
+ (response) => response,
93
+
94
+ async (error) => {
95
+ const { response } = error;
96
+
97
+ // Network Error
98
+ if (!response) {
99
+ toast.error("Network error — please check your connection.");
100
+ return Promise.reject(error);
101
+ }
102
+
103
+ const status = response.status;
104
+
105
+ // 401 Unauthorized
106
+ if (status === 401) {
107
+ toast.error("Session expired. Please log in again.");
108
+ removeLocalStorage("${tokenKey}");
109
+ removeSessionStorage("${tokenKey}");
110
+ if (typeof window !== "undefined" && window.location.pathname !== "/auth/login") {
111
+ window.location.href = "/auth/login";
112
+ }
113
+
114
+ return Promise.reject(error);
115
+ }
116
+
117
+ // 403 Forbidden
118
+ if (status === 403) {
119
+ toast.error("You don’t have permission to access this resource.");
120
+ return Promise.reject(error);
121
+ }
122
+
123
+ // 400 Bad Request
124
+ if (status === 400) {
125
+ toast.error(response.data?.message || "Invalid request.");
126
+ return Promise.reject(error);
127
+ }
128
+
129
+ // 404 Not Found
130
+ if (status === 404) {
131
+ toast.error("Requested resource not found.");
132
+ return Promise.reject(error);
133
+ }
134
+
135
+ // 429 Too Many Requests
136
+ if (status === 429) {
137
+ toast.error("Too many requests — please slow down.");
138
+ return Promise.reject(error);
139
+ }
140
+
141
+ // 500+ Server Error
142
+ if (status >= 500) {
143
+ toast.error("Server error — please try again later.");
144
+ return Promise.reject(error);
145
+ }
146
+
147
+ // Default
148
+ toast.error(response.data?.message || "Something went wrong.");
149
+ return Promise.reject(error);
150
+ }
151
+ );
152
+
153
+ export default useAxiosPrivate;
154
+ `;
155
+
156
+ fs.writeFileSync(
157
+ path.join(axiosDir, `useAxiosPublic.${ext}`),
158
+ publicHookContent,
159
+ );
160
+ fs.writeFileSync(
161
+ path.join(axiosDir, `useAxiosPrivate.${ext}`),
162
+ privateHookContent,
163
+ );
164
+
165
+ console.log(` ✅ Axios hooks created with dynamic token key: ${tokenKey}`);
166
+ }
@@ -0,0 +1,23 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default function cleanupFiles(projectName) {
5
+ console.log("Cleaning up default Next.js files...");
6
+
7
+ const filesToRemove = [
8
+ "public/next.svg",
9
+ "public/vercel.svg",
10
+ ];
11
+
12
+ filesToRemove.forEach((file) => {
13
+ try {
14
+ const filePath = path.join(projectName, file);
15
+ if (fs.existsSync(filePath)) {
16
+ fs.unlinkSync(filePath);
17
+ console.log(` ✓ Removed ${file}`);
18
+ }
19
+ } catch (err) {
20
+ // skipping
21
+ }
22
+ });
23
+ }
@@ -0,0 +1,113 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "tsx" : "jsx";
6
+ const sharedDir = path.join(projectName, "src", "shared", "dashboardShared");
7
+
8
+ if (!fs.existsSync(sharedDir)) {
9
+ fs.mkdirSync(sharedDir, { recursive: true });
10
+ }
11
+
12
+ // 1. Sidebar Component (Your Professional Design)
13
+ const sidebarContent = `'use client';
14
+
15
+ import React from 'react';
16
+ import { LayoutDashboard, Settings, User, BookOpen, Bell, Search } from 'lucide-react';
17
+ import Link from 'next/link';
18
+ import { usePathname } from 'next/navigation';
19
+
20
+ const Sidebar = () => {
21
+ const pathname = usePathname();
22
+
23
+ const menuItems = [
24
+ { icon: <LayoutDashboard size={20} />, label: 'Dashboard', href: '/dashboard/overview' },
25
+ { icon: <BookOpen size={20} />, label: 'My Bookings', href: '/dashboard/my-bookings' },
26
+ { icon: <User size={20} />, label: 'Payments History', href: '/dashboard/payments-history' },
27
+ { icon: <Bell size={20} />, label: 'My Quote Requests', href: '/dashboard/my-quote-requests' },
28
+ { icon: <Settings size={20} />, label: 'Settings', href: '/dashboard/settings' },
29
+ ];
30
+
31
+ return (
32
+ <div className="h-full flex flex-col bg-white">
33
+ <Link href="/" className="p-6 flex items-center gap-2">
34
+ <div className="h-8 w-8 rounded-lg bg-primary flex items-center justify-center">
35
+ <span className="text-primary-foreground font-bold text-xl leading-none">B</span>
36
+ </div>
37
+ <span className="text-xl font-bold tracking-tight text-primary">BlitzStack</span>
38
+ </Link>
39
+
40
+ <nav className="flex-1 px-4 space-y-2 overflow-y-auto">
41
+ {menuItems.map((item, index) => {
42
+ const isActive = pathname === item.href;
43
+ return (
44
+ <Link
45
+ key={index}
46
+ href={item.href}
47
+ className={\`flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer transition-all duration-300 border w-full
48
+ \${isActive
49
+ ? 'bg-linear-to-br from-white/40 to-white/10 backdrop-blur-md border-white/30 text-primary shadow-[0_8px_32px_0_rgba(99,102,241,0.1)]'
50
+ : 'text-gray-500 hover:bg-white/40 border-transparent hover:border-white/20'
51
+ }\`}
52
+ >
53
+ {item.icon}
54
+ <span className="font-medium">{item.label}</span>
55
+ </Link>
56
+ )
57
+ })}
58
+ </nav>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ export default Sidebar;
64
+ `;
65
+
66
+ // 2. TopNavbar Component (Your Professional Design)
67
+ const navbarContent = `'use client';
68
+
69
+ import React from 'react';
70
+ import { Search, Bell, User } from 'lucide-react';
71
+ import { Button } from "@/components/ui/button";
72
+
73
+ const TopNavbar = () => {
74
+ return (
75
+ <div className="flex w-full items-center justify-between">
76
+ <div className="flex flex-col">
77
+ <h1 className="text-xl font-bold tracking-tight text-foreground">Overview</h1>
78
+ <p className="text-sm text-muted-foreground">Welcome back, Jahirul!</p>
79
+ </div>
80
+
81
+ <div className="flex items-center gap-4">
82
+ {/* Simple Search (Placeholder) */}
83
+ <div className="relative hidden md:block">
84
+ <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
85
+ <input
86
+ type="text"
87
+ placeholder="Search anything..."
88
+ className="h-10 w-64 rounded-xl border bg-muted/30 pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 text-foreground shadow-sm"
89
+ />
90
+ </div>
91
+
92
+ {/* Action Buttons */}
93
+ <Button variant="ghost" size="icon" className="rounded-xl">
94
+ <Bell className="h-5 w-5" />
95
+ </Button>
96
+ <div className="h-10 w-10 rounded-xl bg-primary/10 p-2 flex items-center justify-center border border-primary/20">
97
+ <User className="h-5 w-5 text-primary" />
98
+ </div>
99
+ </div>
100
+ </div>
101
+ );
102
+ };
103
+
104
+ export default TopNavbar;
105
+ `;
106
+
107
+ fs.writeFileSync(path.join(sharedDir, `Sidebar.${ext}`), sidebarContent);
108
+ fs.writeFileSync(path.join(sharedDir, `TopNavbar.${ext}`), navbarContent);
109
+
110
+ console.log(
111
+ " ✅ Dashboard shared components (Professional Sidebar, TopNavbar) created.",
112
+ );
113
+ }
@@ -0,0 +1,8 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName) {
5
+ // Create an empty .env file
6
+ fs.writeFileSync(path.join(projectName, ".env"), "");
7
+ console.log(" ✅ .env file created.");
8
+ }
@@ -0,0 +1,36 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName) {
5
+ const baseDir = path.join(projectName, "src");
6
+
7
+ const folders = [
8
+ "app/(main)",
9
+ "app/auth/login",
10
+ "app/dashboard/overview",
11
+ "assets/images",
12
+ "assets/videos",
13
+ "components/common",
14
+ "components/landingPageComponents",
15
+ "components/authPageComponents",
16
+ "components/dashboardPageComponents",
17
+ "hooks/Axios",
18
+ "providers",
19
+ "redux/slices",
20
+ "shared/landingShared",
21
+ "shared/authShared",
22
+ "shared/dashboardShared",
23
+ "utils",
24
+ ];
25
+
26
+ folders.forEach((folder) => {
27
+ const dir = path.join(baseDir, folder);
28
+ if (!fs.existsSync(dir)) {
29
+ fs.mkdirSync(dir, { recursive: true });
30
+ }
31
+ });
32
+
33
+ console.log(
34
+ " ✅ Folder structure updated: (main), auth/login, and dashboard/overview.",
35
+ );
36
+ }
@@ -0,0 +1,43 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName) {
5
+ const gitignoreContent = `# dependencies
6
+ /node_modules
7
+ /.pnp
8
+ .pnp.js
9
+
10
+ # testing
11
+ /coverage
12
+
13
+ # next.js
14
+ /.next/
15
+ /out/
16
+
17
+ # production
18
+ /build
19
+
20
+ # misc
21
+ .DS_Store
22
+ *.pem
23
+
24
+ # debug
25
+ npm-debug.log*
26
+ yarn-debug.log*
27
+ yarn-error.log*
28
+
29
+ # local env files
30
+ .env*.local
31
+ .env
32
+
33
+ # vercel
34
+ .vercel
35
+
36
+ # typescript
37
+ *.tsbuildinfo
38
+ next-env.d.ts
39
+ `;
40
+
41
+ fs.writeFileSync(path.join(projectName, ".gitignore"), gitignoreContent);
42
+ console.log(" ✅ .gitignore created/updated.");
43
+ }
File without changes
@@ -0,0 +1,186 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "tsx" : "jsx";
6
+ const appDir = path.join(projectName, "src", "app");
7
+
8
+ // 1. Remove default page.js or page.tsx
9
+ const defaultPageTypes = ["page.js", "page.tsx", "page.jsx"];
10
+ defaultPageTypes.forEach((file) => {
11
+ const filePath = path.join(appDir, file);
12
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
13
+ });
14
+
15
+ // Helper to create layout
16
+ const createLayoutFile = (dirPath, content) => {
17
+ const targetDir = path.join(appDir, dirPath);
18
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
19
+ fs.writeFileSync(path.join(targetDir, `layout.${ext}`), content);
20
+ };
21
+
22
+ // --- Main Layout ---
23
+ const mainLayoutContent = `export default function MainLayout({ children }${isTypeScript ? ": { children: React.ReactNode }" : ""}) {
24
+ return (
25
+ <div className="main-layout">
26
+ {children}
27
+ </div>
28
+ );
29
+ }
30
+ `;
31
+ createLayoutFile("(main)", mainLayoutContent);
32
+
33
+ // --- Dashboard Layout (Your Professional Design) ---
34
+ const dashboardLayoutContent = `import Sidebar from "@/shared/dashboardShared/Sidebar";
35
+ import TopNavbar from "@/shared/dashboardShared/TopNavbar";
36
+ import React from "react";
37
+
38
+ const DashboardLayout = ({ children }${isTypeScript ? ": { children: React.ReactNode }" : ""}) => {
39
+ return (
40
+ <main className="w-full h-screen flex relative bg-background">
41
+ <div className="xl:w-[280px] h-screen xl:block hidden border-r shrink-0">
42
+ <Sidebar />
43
+ </div>
44
+
45
+ {/* Main Content */}
46
+ <div className="flex flex-col w-full xl:w-[calc(100%-280px)] h-screen overflow-hidden">
47
+ <div className="w-full h-auto xl:h-[80px] flex flex-col justify-center px-4 md:px-8 py-4 bg-white/50 backdrop-blur-md xl:bg-transparent border-b">
48
+ <TopNavbar />
49
+ </div>
50
+
51
+ <div className="w-full h-[calc(100%-80px)] overflow-auto custom-scrollbar p-10 relative">
52
+ <div className="relative z-10">
53
+ {children}
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </main>
58
+ );
59
+ };
60
+
61
+ export default DashboardLayout;
62
+ `;
63
+ createLayoutFile("dashboard", dashboardLayoutContent);
64
+
65
+ // --- Auth Layout ---
66
+ const authLayoutContent = `export default function AuthLayout({ children }${isTypeScript ? ": { children: React.ReactNode }" : ""}) {
67
+ return (
68
+ <div className="auth-layout flex min-h-screen items-center justify-center bg-muted/50">
69
+ {children}
70
+ </div>
71
+ );
72
+ }
73
+ `;
74
+ createLayoutFile("auth", authLayoutContent);
75
+
76
+ // --- Create Pages ---
77
+
78
+ // 1. Landing Page (Keeping your professional design)
79
+ const landingPageContent = `import Link from "next/link";
80
+ import { Button } from "@/components/ui/button";
81
+ import { ExternalLink, LayoutDashboard, LogIn, Sparkles } from "lucide-react";
82
+
83
+ export default function LandingPage() {
84
+ return (
85
+ <div className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-background px-6">
86
+ {/* Subtle Background Radial Gradient */}
87
+ <div className="absolute inset-x-0 top-0 -z-10 h-full w-full bg-[radial-gradient(circle_at_center,var(--color-primary)_0%,transparent_70%)] opacity-[0.03]" />
88
+
89
+ <div className="flex flex-col items-center text-center">
90
+ {/* Modern Icon/Badge */}
91
+ <div className="mb-8 flex h-16 w-16 items-center justify-center rounded-2xl bg-primary/10 text-primary">
92
+ <Sparkles className="h-8 w-8" />
93
+ </div>
94
+
95
+ {/* Main Heading */}
96
+ <h1 className="mb-4 text-5xl font-extrabold tracking-tight text-foreground sm:text-7xl">
97
+ <span className="text-primary tracking-tighter">Your Next.js</span> Project <br />
98
+ is <span className="bg-linear-to-r from-primary to-chart-1 bg-clip-text text-transparent">Ready</span>
99
+ </h1>
100
+
101
+ {/* Subtext */}
102
+ <p className="mb-10 max-w-lg text-lg text-muted-foreground sm:text-xl">
103
+ Everything is set up and configured. Now you can start building your amazing application.
104
+ </p>
105
+
106
+ {/* Navigation Buttons */}
107
+ <div className="flex flex-col gap-4 sm:flex-row">
108
+ <Link href="/auth/login">
109
+ <Button size="lg" className="h-12 gap-2 rounded-xl px-8 font-semibold shadow-lg shadow-primary/20 transition-all hover:scale-105 active:scale-95">
110
+ <LogIn className="h-4 w-4" />
111
+ Go to Login
112
+ </Button>
113
+ </Link>
114
+ <Link href="/dashboard/overview">
115
+ <Button variant="outline" size="lg" className="h-12 gap-2 rounded-xl px-8 font-semibold transition-all hover:scale-105 active:scale-95">
116
+ <LayoutDashboard className="h-4 w-4" />
117
+ Dashboard
118
+ </Button>
119
+ </Link>
120
+ <Link href="https://blitzstack.vercel.app/" target="_blank">
121
+ <Button variant="ghost" size="lg" className="h-12 gap-2 rounded-xl px-8 font-semibold transition-all hover:scale-105 active:scale-95 text-primary border border-primary/10 hover:bg-primary/5">
122
+ <ExternalLink className="h-4 w-4" />
123
+ Visit Website
124
+ </Button>
125
+ </Link>
126
+ </div>
127
+
128
+ {/* Footer Credit */}
129
+ <div className="mt-20 flex flex-col items-center gap-2">
130
+ <p className="text-sm font-medium text-muted-foreground">
131
+ Built by <Link href="https://github.com/jahirul077" target="_blank" className="text-primary hover:underline">Jahirul</Link>
132
+ </p>
133
+ <div className="flex h-1 w-12 rounded-full bg-primary/20" />
134
+ </div>
135
+ </div>
136
+ </div>
137
+ );
138
+ }
139
+ `;
140
+ const mainDir = path.join(appDir, "(main)");
141
+ if (!fs.existsSync(mainDir)) fs.mkdirSync(mainDir, { recursive: true });
142
+ fs.writeFileSync(path.join(mainDir, `page.${ext}`), landingPageContent);
143
+
144
+ // 2. Overview Page (Simplified as requested)
145
+ const dashboardOverviewDir = path.join(appDir, "dashboard/overview");
146
+ if (!fs.existsSync(dashboardOverviewDir))
147
+ fs.mkdirSync(dashboardOverviewDir, { recursive: true });
148
+ fs.writeFileSync(
149
+ path.join(dashboardOverviewDir, `page.${ext}`),
150
+ `import Link from "next/link";
151
+
152
+ export default function OverviewPage() {
153
+ return (
154
+ <div className="flex flex-col items-center justify-center min-h-[60vh] gap-4">
155
+ <h1 className="text-4xl font-bold tracking-tight">Overview Page </h1>
156
+ <Link href="/" className="text-primary hover:underline font-medium italic text-xl">
157
+ Go to Home
158
+ </Link>
159
+ </div>
160
+ );
161
+ }`,
162
+ );
163
+
164
+ // 3. Login Page (Simplified as requested)
165
+ const loginDir = path.join(appDir, "auth/login");
166
+ if (!fs.existsSync(loginDir)) fs.mkdirSync(loginDir, { recursive: true });
167
+ fs.writeFileSync(
168
+ path.join(loginDir, `page.${ext}`),
169
+ `import Link from "next/link";
170
+
171
+ export default function LoginPage() {
172
+ return (
173
+ <div className="flex flex-col items-center justify-center p-24 gap-4">
174
+ <h1 className="text-4xl font-bold tracking-tight">Login Page </h1>
175
+ <Link href="/" className="text-primary hover:underline font-medium italic text-xl">
176
+ Go to Home
177
+ </Link>
178
+ </div>
179
+ );
180
+ }`,
181
+ );
182
+
183
+ console.log(
184
+ " ✅ Routes updated: Login and Overview simplified as requested.",
185
+ );
186
+ }
@@ -0,0 +1,44 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "tsx" : "jsx";
6
+ const appDir = path.join(projectName, "src", "app");
7
+ const layoutFilename = isTypeScript ? "layout.tsx" : "layout.js";
8
+ const layoutPath = path.join(appDir, layoutFilename);
9
+
10
+ const rootLayoutContent = `import "./globals.css";
11
+ import ReduxProvider from "@/providers/ReduxProvider";
12
+ import ReactQueryProvider from "@/providers/ReactQueryProvider";
13
+ import { Toaster } from "sonner";
14
+
15
+ export const metadata = {
16
+ title: "Professional Next.js Stack",
17
+ description: "Generated by Next.js Stack CLI",
18
+ };
19
+
20
+ export default function RootLayout({
21
+ children,
22
+ }${isTypeScript ? ": { children: React.ReactNode }" : ""}) {
23
+ return (
24
+ <html lang="en">
25
+ <body className="antialiased">
26
+ <ReduxProvider>
27
+ <ReactQueryProvider>
28
+ {children}
29
+ <Toaster richColors position="top-right" />
30
+ </ReactQueryProvider>
31
+ </ReduxProvider>
32
+ </body>
33
+ </html>
34
+ );
35
+ }
36
+ `;
37
+
38
+ // Overwrite the root layout to remove custom fonts and use browser default
39
+ fs.writeFileSync(layoutPath, rootLayoutContent);
40
+
41
+ console.log(
42
+ ` ✅ Root Layout (${layoutFilename}) updated: Removed Google Fonts to use browser defaults.`,
43
+ );
44
+ }
@@ -0,0 +1,9 @@
1
+ import { execSync } from "child_process";
2
+
3
+ export default async function setup(projectName) {
4
+ // Optional: Install Radix UI primitives as needed
5
+ // run(`cd ${projectName} && npm install @radix-ui/react-navigation-menu @radix-ui/react-dropdown-menu`);
6
+ console.log(
7
+ " ✅ Radix UI setup placeholder (Add your preferred primitives here).",
8
+ );
9
+ }
@@ -0,0 +1,34 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "tsx" : "jsx";
6
+ const providersDir = path.join(projectName, "src", "providers");
7
+ if (!fs.existsSync(providersDir))
8
+ fs.mkdirSync(providersDir, { recursive: true });
9
+
10
+ const queryProvider = `"use client";
11
+
12
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
13
+ import { useState } from "react";
14
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
15
+
16
+
17
+ export default function ReactQueryProvider({ children }${isTypeScript ? ": { children: React.ReactNode }" : ""}) {
18
+ const [queryClient] = useState(() => new QueryClient());
19
+
20
+ return (
21
+ <QueryClientProvider client={queryClient}>
22
+ <ReactQueryDevtools initialIsOpen={false} />
23
+ {children}
24
+ </QueryClientProvider>
25
+ );
26
+ }
27
+ `;
28
+
29
+ fs.writeFileSync(
30
+ path.join(providersDir, `ReactQueryProvider.${ext}`),
31
+ queryProvider,
32
+ );
33
+ console.log(" ✅ ReactQueryProvider updated with Devtools.");
34
+ }
@@ -0,0 +1,79 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "ts" : "js";
6
+ const jsxExt = isTypeScript ? "tsx" : "jsx";
7
+ const reduxDir = path.join(projectName, "src", "redux");
8
+ const providersDir = path.join(projectName, "src", "providers");
9
+ const slicesDir = path.join(reduxDir, "slices");
10
+
11
+ if (!fs.existsSync(slicesDir)) fs.mkdirSync(slicesDir, { recursive: true });
12
+ if (!fs.existsSync(providersDir))
13
+ fs.mkdirSync(providersDir, { recursive: true });
14
+
15
+ // 1. Create Counter Slice only
16
+ const counterSliceContent = `import { createSlice } from "@reduxjs/toolkit";
17
+
18
+ const counterSlice = createSlice({
19
+ name: "counter",
20
+ initialState: { value: 0 },
21
+ reducers: {
22
+ increment: (state) => {
23
+ state.value += 1;
24
+ },
25
+ decrement: (state) => {
26
+ state.value -= 1;
27
+ },
28
+ incrementByAmount: (state, action) => {
29
+ state.value += action.payload;
30
+ },
31
+ },
32
+ });
33
+
34
+ export const { increment, decrement, incrementByAmount } = counterSlice.actions;
35
+ export default counterSlice.reducer;
36
+ `;
37
+
38
+ fs.writeFileSync(
39
+ path.join(slicesDir, `counterSlice.${ext}`),
40
+ counterSliceContent,
41
+ );
42
+
43
+ // 2. Store configuration
44
+ const storeConfig = `import { configureStore } from "@reduxjs/toolkit";
45
+ import counterReducer from "./slices/counterSlice";
46
+
47
+ export const store = configureStore({
48
+ reducer: {
49
+ counter: counterReducer,
50
+ },
51
+ });
52
+
53
+ ${isTypeScript ? "export type RootState = ReturnType<typeof store.getState>;\nexport type AppDispatch = typeof store.dispatch;" : ""}
54
+ `;
55
+
56
+ fs.writeFileSync(path.join(reduxDir, `store.${ext}`), storeConfig);
57
+
58
+ // 3. Redux Provider
59
+ const reduxProviderContent = `"use client";
60
+
61
+ import { Provider } from "react-redux";
62
+ import { store } from "@/redux/store";
63
+
64
+ export default function ReduxProvider({ children }${isTypeScript ? ": { children: React.ReactNode }" : ""}) {
65
+ return (
66
+ <Provider store={store}>
67
+ {children}
68
+ </Provider>
69
+ );
70
+ }
71
+ `;
72
+
73
+ fs.writeFileSync(
74
+ path.join(providersDir, `ReduxProvider.${jsxExt}`),
75
+ reduxProviderContent,
76
+ );
77
+
78
+ console.log(" ✅ Redux setup updated with counterSlice only.");
79
+ }
File without changes
File without changes
@@ -0,0 +1,90 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName, isTypeScript) {
5
+ const ext = isTypeScript ? "ts" : "js";
6
+ const utilsDir = path.join(projectName, "src", "utils");
7
+
8
+ if (!fs.existsSync(utilsDir)) {
9
+ fs.mkdirSync(utilsDir, { recursive: true });
10
+ }
11
+
12
+ // 1. cn helper
13
+ const cnContent = `import { clsx ${isTypeScript ? ", type ClassValue" : ""} } from "clsx";
14
+ import { twMerge } from "tailwind-merge";
15
+
16
+ export function cn(...inputs${isTypeScript ? ": ClassValue[]" : ""}) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+ `;
20
+
21
+ // 2. localStorage helper
22
+ const localStoreContent = `export const setLocalStorage = (key${isTypeScript ? ": string" : ""}, value${isTypeScript ? ": any" : ""}) => {
23
+ if (typeof window !== "undefined") {
24
+ localStorage.setItem(key, JSON.stringify(value));
25
+ }
26
+ };
27
+
28
+ export const getLocalStorage = (key${isTypeScript ? ": string" : ""}) => {
29
+ if (typeof window !== "undefined") {
30
+ const data = localStorage.getItem(key);
31
+ if (data) {
32
+ try {
33
+ return JSON.parse(data);
34
+ } catch (err) {
35
+ return data;
36
+ }
37
+ }
38
+ }
39
+ return null;
40
+ };
41
+
42
+ export const removeLocalStorage = (key${isTypeScript ? ": string" : ""}) => {
43
+ if (typeof window !== "undefined") {
44
+ localStorage.removeItem(key);
45
+ }
46
+ };
47
+ `;
48
+
49
+ // 3. sessionStorage helper
50
+ const sessionStoreContent = `export const setSessionStorage = (key${isTypeScript ? ": string" : ""}, value${isTypeScript ? ": any" : ""}) => {
51
+ if (typeof window !== "undefined") {
52
+ sessionStorage.setItem(key, JSON.stringify(value));
53
+ }
54
+ };
55
+
56
+ export const getSessionStorage = (key${isTypeScript ? ": string" : ""}) => {
57
+ if (typeof window !== "undefined") {
58
+ const data = sessionStorage.getItem(key);
59
+ if (data) {
60
+ try {
61
+ return JSON.parse(data);
62
+ } catch (err) {
63
+ return data;
64
+ }
65
+ }
66
+ }
67
+ return null;
68
+ };
69
+
70
+ export const removeSessionStorage = (key${isTypeScript ? ": string" : ""}) => {
71
+ if (typeof window !== "undefined") {
72
+ sessionStorage.removeItem(key);
73
+ }
74
+ };
75
+ `;
76
+
77
+ fs.writeFileSync(path.join(utilsDir, `cn.${ext}`), cnContent);
78
+ fs.writeFileSync(
79
+ path.join(utilsDir, `localStorage.${ext}`),
80
+ localStoreContent,
81
+ );
82
+ fs.writeFileSync(
83
+ path.join(utilsDir, `sessionStorage.${ext}`),
84
+ sessionStoreContent,
85
+ );
86
+
87
+ console.log(
88
+ " ✅ Utility helpers (cn, localStorage, sessionStorage) created in src/utils.",
89
+ );
90
+ }
@@ -0,0 +1,11 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export default async function setup(projectName) {
5
+ const vercelConfig = `{
6
+ "rewrites": [{ "source": "/(.*)", "destination": "/" }]
7
+ }`;
8
+
9
+ fs.writeFileSync(path.join(projectName, "vercel.json"), vercelConfig);
10
+ console.log(" ✅ vercel.json created.");
11
+ }
package/text.txt ADDED
@@ -0,0 +1 @@
1
+ create-next-stack my-awesome-app