stackkit 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/README.md +50 -42
  2. package/dist/cli/add.js +122 -56
  3. package/dist/cli/create.d.ts +2 -0
  4. package/dist/cli/create.js +271 -95
  5. package/dist/cli/doctor.js +1 -0
  6. package/dist/cli/list.d.ts +1 -1
  7. package/dist/cli/list.js +6 -4
  8. package/dist/index.js +234 -191
  9. package/dist/lib/constants.d.ts +4 -0
  10. package/dist/lib/constants.js +4 -0
  11. package/dist/lib/discovery/module-discovery.d.ts +4 -0
  12. package/dist/lib/discovery/module-discovery.js +56 -0
  13. package/dist/lib/generation/code-generator.d.ts +11 -2
  14. package/dist/lib/generation/code-generator.js +42 -3
  15. package/dist/lib/generation/generator-utils.js +3 -1
  16. package/dist/lib/pm/package-manager.js +16 -13
  17. package/dist/lib/ui/logger.js +3 -2
  18. package/dist/lib/utils/path-resolver.d.ts +2 -0
  19. package/dist/lib/utils/path-resolver.js +8 -0
  20. package/dist/meta.json +8312 -0
  21. package/modules/auth/better-auth/files/{shared → express}/config/env.ts +48 -50
  22. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +20 -1
  23. package/modules/auth/better-auth/files/express/modules/auth.controller.ts +349 -0
  24. package/modules/auth/better-auth/files/express/modules/{auth/auth.route.ts → auth.route.ts} +9 -4
  25. package/modules/auth/better-auth/files/express/modules/auth.service.ts +664 -0
  26. package/modules/auth/better-auth/files/express/modules/{auth/auth.type.ts → auth.type.ts} +22 -9
  27. package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
  28. package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
  29. package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
  30. package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
  31. package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
  32. package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
  33. package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
  34. package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
  35. package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
  36. package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
  37. package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
  38. package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
  39. package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
  40. package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
  41. package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
  42. package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
  43. package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
  44. package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
  45. package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
  46. package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
  47. package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
  48. package/modules/auth/better-auth/files/nextjs/proxy.ts +154 -42
  49. package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
  50. package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
  51. package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
  52. package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
  53. package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
  54. package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
  55. package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
  56. package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
  57. package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
  58. package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
  59. package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
  60. package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
  61. package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
  62. package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
  63. package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
  64. package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
  65. package/modules/auth/better-auth/files/react/router.tsx +73 -0
  66. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
  67. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
  68. package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
  69. package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
  70. package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
  71. package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
  72. package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
  73. package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
  74. package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
  75. package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
  76. package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
  77. package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
  78. package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
  79. package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
  80. package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
  81. package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
  82. package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
  83. package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
  84. package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
  85. package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
  86. package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
  87. package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
  88. package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
  89. package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
  90. package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
  91. package/modules/auth/better-auth/files/shared/lib/auth.ts +5 -19
  92. package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
  93. package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
  94. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
  95. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
  96. package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
  97. package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
  98. package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
  99. package/modules/auth/better-auth/files/shared/utils/email.ts +2 -17
  100. package/modules/auth/better-auth/generator.json +172 -51
  101. package/modules/auth/better-auth/module.json +2 -2
  102. package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
  103. package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
  104. package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
  105. package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
  106. package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
  107. package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
  108. package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
  109. package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
  110. package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
  111. package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
  112. package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
  113. package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
  114. package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
  115. package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
  116. package/modules/components/files/shared/url/load-more.tsx +93 -0
  117. package/modules/components/files/shared/url/search-bar.tsx +131 -0
  118. package/modules/components/files/shared/url/sort-select.tsx +118 -0
  119. package/modules/components/files/shared/url/url-tabs.tsx +77 -0
  120. package/modules/components/generator.json +109 -0
  121. package/modules/components/module.json +11 -0
  122. package/modules/database/mongoose/generator.json +3 -14
  123. package/modules/database/mongoose/module.json +2 -2
  124. package/modules/database/prisma/generator.json +6 -12
  125. package/modules/database/prisma/module.json +2 -2
  126. package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
  127. package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
  128. package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
  129. package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
  130. package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
  131. package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
  132. package/modules/storage/cloudinary/generator.json +34 -0
  133. package/modules/storage/cloudinary/module.json +11 -0
  134. package/modules/ui/shadcn/generator.json +21 -0
  135. package/modules/ui/shadcn/module.json +11 -0
  136. package/package.json +24 -26
  137. package/templates/express/README.md +11 -16
  138. package/templates/express/src/config/env.ts +7 -5
  139. package/templates/nextjs/README.md +13 -18
  140. package/templates/nextjs/app/favicon.ico +0 -0
  141. package/templates/nextjs/app/layout.tsx +6 -4
  142. package/templates/nextjs/components/providers/query-provider.tsx +3 -0
  143. package/templates/nextjs/env.example +3 -1
  144. package/templates/nextjs/lib/axios/http.ts +23 -0
  145. package/templates/nextjs/lib/env.ts +7 -5
  146. package/templates/nextjs/package.json +2 -1
  147. package/templates/nextjs/template.json +1 -2
  148. package/templates/react/README.md +9 -14
  149. package/templates/react/index.html +1 -1
  150. package/templates/react/package.json +1 -1
  151. package/templates/react/src/assets/favicon.ico +0 -0
  152. package/templates/react/src/components/providers/query-provider.tsx +38 -0
  153. package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
  154. package/templates/react/src/lib/axios/http.ts +24 -0
  155. package/templates/react/src/main.tsx +8 -11
  156. package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
  157. package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
  158. package/templates/react/src/router.tsx +6 -6
  159. package/templates/react/src/vite-env.d.ts +2 -1
  160. package/templates/react/template.json +0 -1
  161. package/templates/react/tsconfig.app.json +6 -0
  162. package/templates/react/tsconfig.json +7 -1
  163. package/templates/react/vite.config.ts +12 -0
  164. package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
  165. package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
  166. package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
  167. package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
  168. package/modules/auth/authjs/generator.json +0 -64
  169. package/modules/auth/authjs/module.json +0 -13
  170. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
  171. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -549
  172. package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
  173. package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
  174. package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -31
  175. package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
  176. package/templates/nextjs/lib/api/http.ts +0 -40
  177. package/templates/react/public/vite.svg +0 -1
  178. package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
  179. package/templates/react/src/app/layouts/public-layout.tsx +0 -5
  180. package/templates/react/src/app/providers.tsx +0 -20
  181. package/templates/react/src/app/router.tsx +0 -21
  182. package/templates/react/src/assets/react.svg +0 -1
  183. package/templates/react/src/shared/api/http.ts +0 -39
  184. package/templates/react/src/shared/components/loading.tsx +0 -8
  185. package/templates/react/src/shared/lib/query-client.ts +0 -12
  186. package/templates/react/src/utils/storage.ts +0 -35
  187. package/templates/react/src/utils/utils.ts +0 -3
  188. /package/modules/auth/better-auth/files/{shared/mongoose/auth/constants.ts → express/mongo-modules/auth.constants.ts} +0 -0
  189. /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
  190. /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
  191. /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
  192. /package/templates/react/src/{shared/pages → pages}/not-found.tsx +0 -0
@@ -1,6 +1,6 @@
1
- # Next.js Template
1
+ # Next.js App
2
2
 
3
- Production-ready Next.js starter with TypeScript and App Router.
3
+ Production-ready Next.js starter with TypeScript and App Router, generated by [StackKit](https://github.com/tariqul420/stackkit).
4
4
 
5
5
  ## Requirements
6
6
 
@@ -30,10 +30,12 @@ npm run dev
30
30
 
31
31
  ## Scripts
32
32
 
33
- - `npm run dev` - Start development server
34
- - `npm run build` - Build for production
35
- - `npm run start` - Start production server
36
- - `npm run lint` - Run linter
33
+ | Command | Description |
34
+ | ------------ | ------------------------ |
35
+ | `pnpm dev` | Start development server |
36
+ | `pnpm build` | Build for production |
37
+ | `pnpm start` | Start production server |
38
+ | `pnpm lint` | Run ESLint |
37
39
 
38
40
  ## Recommended Folder & File Structure
39
41
 
@@ -137,20 +139,13 @@ Create a `.env.local` (Next.js) file for local environment variables. Keep secre
137
139
 
138
140
  ## Deployment
139
141
 
142
+ Deploy to Vercel, Netlify, or any Node.js hosting service:
143
+
140
144
  ```bash
141
- npm run build
142
- npm run start
145
+ pnpm build
146
+ pnpm start
143
147
  ```
144
148
 
145
- Deploy to Vercel, Netlify, or any Node.js hosting service.
146
-
147
149
  ---
148
150
 
149
- ## Generated with StackKit
150
-
151
- This project was scaffolded using **StackKit** — a CLI toolkit for building production-ready applications.
152
-
153
- - Generated via: `npx stackkit@latest create`
154
-
155
- Learn more about StackKit:
156
- https://github.com/tariqul420/stackkit
151
+ Generated with [StackKit](https://github.com/tariqul420/stackkit) — `npx stackkit@latest create`
Binary file
@@ -4,14 +4,16 @@ import { Geist, Geist_Mono } from "next/font/google";
4
4
  import { Toaster } from "sonner";
5
5
  import "./globals.css";
6
6
 
7
- const geistSans = Geist({
8
- variable: "--font-geist-sans",
7
+ const geist = Geist({
8
+ variable: "--font-sans",
9
9
  subsets: ["latin"],
10
+ weight: ["400", "500", "600", "700", "800", "900"],
10
11
  });
11
12
 
12
13
  const geistMono = Geist_Mono({
13
14
  variable: "--font-geist-mono",
14
15
  subsets: ["latin"],
16
+ weight: ["400", "500", "600", "700", "800", "900"],
15
17
  });
16
18
 
17
19
  export const metadata: Metadata = {
@@ -25,9 +27,9 @@ export default function RootLayout({
25
27
  children: React.ReactNode;
26
28
  }>) {
27
29
  return (
28
- <html lang="en">
30
+ <html lang="en" suppressHydrationWarning className={`${geist.variable} ${geistMono.variable}`}>
29
31
  <head />
30
- <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
32
+ <body className="font-sans antialiased" suppressHydrationWarning>
31
33
  <QueryProviders>{children}</QueryProviders>
32
34
  <Toaster theme="system" position="top-right" richColors />
33
35
  </body>
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { isServer, QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
+ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
4
5
  import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental";
5
6
 
6
7
  function makeQueryClient() {
@@ -26,10 +27,12 @@ function getQueryClient() {
26
27
 
27
28
  function QueryProviders({ children }: { children: React.ReactNode }) {
28
29
  const queryClient = getQueryClient();
30
+ const isProduction = process.env.NODE_ENV === "production";
29
31
 
30
32
  return (
31
33
  <QueryClientProvider client={queryClient}>
32
34
  <ReactQueryStreamedHydration>{children}</ReactQueryStreamedHydration>
35
+ {!isProduction && <ReactQueryDevtools initialIsOpen={false} />}
33
36
  </QueryClientProvider>
34
37
  );
35
38
  }
@@ -1 +1,3 @@
1
- NEXT_PUBLIC_APP_URL=http://localhost:3000
1
+ NEXT_PUBLIC_APP_NAME=My App
2
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
3
+ NEXT_PUBLIC_API_URL=http://localhost:5000/api
@@ -0,0 +1,23 @@
1
+ import axios from "axios";
2
+ import { envVars } from "../env";
3
+
4
+ const api = axios.create({
5
+ baseURL: envVars.API_URL || "http://localhost:5000/api",
6
+ withCredentials: true,
7
+ timeout: 30000,
8
+ headers: {
9
+ "Content-Type": "application/json",
10
+ },
11
+ });
12
+
13
+ api.interceptors.request.use(
14
+ (config) => config,
15
+ (error) => Promise.reject(error),
16
+ );
17
+
18
+ api.interceptors.response.use(
19
+ (response) => response,
20
+ (error) => Promise.reject(error),
21
+ );
22
+
23
+ export default api;
@@ -1,18 +1,20 @@
1
1
  interface EnvVars {
2
+ APP_NAME: string;
2
3
  APP_URL: string;
3
4
  API_URL: string;
4
5
  }
5
6
 
6
7
  const loadEnvVars = (): EnvVars => {
7
- const requiredEnvVars = ["NEXT_PUBLIC_API_URL"];
8
+ const requiredVars: (keyof EnvVars)[] = ["APP_NAME", "APP_URL", "API_URL"];
8
9
 
9
- requiredEnvVars.forEach((varName) => {
10
- if (!process.env[varName]) {
11
- throw new Error(`Environment variable ${varName} is required but not defined.`);
10
+ for (const varName of requiredVars) {
11
+ if (!process.env[`NEXT_PUBLIC_${varName}`]) {
12
+ console.warn(`Environment variable NEXT_PUBLIC_${varName} is not set. Using default value.`);
12
13
  }
13
- });
14
+ }
14
15
 
15
16
  return {
17
+ APP_NAME: process.env.NEXT_PUBLIC_APP_NAME || "App Name",
16
18
  APP_URL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
17
19
  API_URL: process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000/api",
18
20
  };
@@ -12,6 +12,7 @@
12
12
  "dependencies": {
13
13
  "@tanstack/react-query": "^5.90.21",
14
14
  "@tanstack/react-query-next-experimental": "^5.91.0",
15
+ "@tanstack/react-query-devtools": "^5.91.2",
15
16
  "axios": "^1.13.5",
16
17
  "next": "16.1.1",
17
18
  "react": "19.2.3",
@@ -28,4 +29,4 @@
28
29
  "tailwindcss": "^4.1.18",
29
30
  "typescript": "^5.9.3"
30
31
  }
31
- }
32
+ }
@@ -4,8 +4,7 @@
4
4
  "framework": "nextjs",
5
5
  "description": "Next.js 16 App Router with TypeScript",
6
6
  "compatibility": {
7
- "databases": ["prisma", "mongoose"],
8
- "auth": ["better-auth", "authjs"]
7
+ "auth": ["better-auth"]
9
8
  },
10
9
  "files": [
11
10
  "app/",
@@ -1,6 +1,6 @@
1
- # React Template
1
+ # React App
2
2
 
3
- Production-ready React starter with TypeScript, Vite, and essential libraries.
3
+ Production-ready React starter with TypeScript, Vite, and essential libraries, generated by [StackKit](https://github.com/tariqul420/stackkit).
4
4
 
5
5
  ## Requirements
6
6
 
@@ -32,10 +32,12 @@ npm run dev
32
32
 
33
33
  ## Scripts
34
34
 
35
- - `pnpm dev` - Start development server
36
- - `pnpm build` - Build for production
37
- - `pnpm preview` - Preview production build
38
- - `pnpm lint` - Run linter
35
+ | Command | Description |
36
+ | -------------- | ------------------------ |
37
+ | `pnpm dev` | Start development server |
38
+ | `pnpm build` | Build for production |
39
+ | `pnpm preview` | Preview production build |
40
+ | `pnpm lint` | Run ESLint |
39
41
 
40
42
  ## Environment Variables
41
43
 
@@ -122,11 +124,4 @@ Deploy the resulting `dist`/build output to your hosting platform (Vercel, Netli
122
124
 
123
125
  ---
124
126
 
125
- ## Generated with StackKit
126
-
127
- This project was scaffolded using **StackKit** — a CLI toolkit for building production-ready applications.
128
-
129
- - Generated via: `npx stackkit@latest create`
130
-
131
- Learn more about StackKit:
132
- https://github.com/tariqul420/stackkit
127
+ Generated with [StackKit](https://github.com/tariqul420/stackkit) — `npx stackkit@latest create`
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
5
+ <link rel="icon" type="image/x-icon" href="/src/assets/favicon.ico" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <link href="/src/index.css" rel="stylesheet">
8
8
  <title>React</title>
@@ -20,7 +20,7 @@
20
20
  "react": "^19.2.3",
21
21
  "react-dom": "^19.2.3",
22
22
  "react-helmet-async": "^2.0.5",
23
- "react-hot-toast": "^2.6.0",
23
+ "sonner": "^2.0.7",
24
24
  "react-router": "^7.12.0",
25
25
  "tailwind-merge": "^3.4.0"
26
26
  },
@@ -0,0 +1,38 @@
1
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2
+ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
3
+ import type { ReactNode } from "react";
4
+
5
+ const isProduction = import.meta.env.PROD;
6
+
7
+ const NO_RETRY_STATUSES = new Set([401, 403, 404, 422]);
8
+
9
+ const queryClient = new QueryClient({
10
+ defaultOptions: {
11
+ queries: {
12
+ staleTime: 1000 * 60 * 5,
13
+ gcTime: 1000 * 60 * 10,
14
+ refetchOnWindowFocus: false,
15
+ retry: (failureCount, error) => {
16
+ if (
17
+ error instanceof Error &&
18
+ NO_RETRY_STATUSES.has(Number((error as Error & { status?: number }).status))
19
+ ) {
20
+ return false;
21
+ }
22
+ return failureCount < 2;
23
+ },
24
+ },
25
+ mutations: {
26
+ retry: false,
27
+ },
28
+ },
29
+ });
30
+
31
+ export default function QueryProvider({ children }: { children: ReactNode }) {
32
+ return (
33
+ <QueryClientProvider client={queryClient}>
34
+ {children}
35
+ {!isProduction && <ReactQueryDevtools initialIsOpen={false} />}
36
+ </QueryClientProvider>
37
+ );
38
+ }
@@ -1,4 +1,4 @@
1
- import { Helmet, HelmetProvider } from "react-helmet-async";
1
+ import { Helmet } from "react-helmet-async";
2
2
 
3
3
  interface SEOProps {
4
4
  title?: string;
@@ -9,16 +9,12 @@ interface SEOProps {
9
9
  }
10
10
 
11
11
  const defaultSEO = {
12
- title: "React App",
13
- description: "A modern React application built with Vite",
14
- keywords: "react, vite, typescript, spa",
12
+ title: "Stackkit - Production-ready React starter template",
13
+ description: "Production-ready React starter template",
14
+ keywords: "react, vite, typescript, stackkit, production-ready, starter template",
15
15
  image: "/og-image.png",
16
16
  };
17
17
 
18
- export function SEOProvider({ children }: { children: React.ReactNode }) {
19
- return <HelmetProvider>{children}</HelmetProvider>;
20
- }
21
-
22
18
  export function SEO({ title, description, keywords, image, url }: SEOProps) {
23
19
  const siteTitle = title ? `${title} | ${defaultSEO.title}` : defaultSEO.title;
24
20
  const siteDescription = description || defaultSEO.description;
@@ -0,0 +1,24 @@
1
+ import axios from "axios";
2
+
3
+ const API_URL = import.meta.env.VITE_API_URL;
4
+
5
+ const api = axios.create({
6
+ baseURL: API_URL || "http://localhost:5000/api",
7
+ withCredentials: true,
8
+ timeout: 30000,
9
+ headers: {
10
+ "Content-Type": "application/json",
11
+ },
12
+ });
13
+
14
+ api.interceptors.request.use(
15
+ (config) => config,
16
+ (error) => Promise.reject(error),
17
+ );
18
+
19
+ api.interceptors.response.use(
20
+ (response) => response,
21
+ (error) => Promise.reject(error),
22
+ );
23
+
24
+ export default api;
@@ -1,22 +1,19 @@
1
- import { QueryClientProvider } from "@tanstack/react-query";
2
- import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
3
1
  import { StrictMode } from "react";
4
2
  import { createRoot } from "react-dom/client";
5
- import { Toaster } from "react-hot-toast";
3
+ import { HelmetProvider } from "react-helmet-async";
6
4
  import { RouterProvider } from "react-router";
5
+ import { Toaster } from "sonner";
6
+ import QueryProvider from "./components/providers/query-provider";
7
7
  import "./index.css";
8
8
  import { router } from "./router";
9
- import { queryClient } from "./shared/lib/query-client";
10
- import { SEOProvider } from "./shared/components/seo";
11
9
 
12
10
  createRoot(document.getElementById("root")!).render(
13
11
  <StrictMode>
14
- <SEOProvider>
15
- <QueryClientProvider client={queryClient}>
12
+ <HelmetProvider>
13
+ <QueryProvider>
16
14
  <RouterProvider router={router} />
17
- <Toaster position="top-right" />
18
- <ReactQueryDevtools initialIsOpen={false} />
19
- </QueryClientProvider>
20
- </SEOProvider>
15
+ <Toaster theme="system" position="top-right" richColors />
16
+ </QueryProvider>
17
+ </HelmetProvider>
21
18
  </StrictMode>,
22
19
  );
@@ -1,4 +1,4 @@
1
- import { SEO } from "../../../shared/components/seo";
1
+ import { SEO } from "../components/seo";
2
2
 
3
3
  export default function About() {
4
4
  return (
@@ -1,4 +1,4 @@
1
- import { SEO } from "../../../shared/components/seo";
1
+ import { SEO } from "../components/seo";
2
2
 
3
3
  export default function Home() {
4
4
  return (
@@ -1,9 +1,9 @@
1
1
  import { createBrowserRouter } from "react-router";
2
- import { ErrorBoundary } from "./components/ErrorBoundary";
3
- import Layout from "./components/Layout";
4
- import About from "./pages/About";
5
- import Home from "./pages/Home";
6
- import NotFound from "./pages/NotFound";
2
+ import { ErrorBoundary } from "./components/error-boundary";
3
+ import Layout from "./components/layout";
4
+ import About from "./pages/about";
5
+ import Home from "./pages/home";
6
+ import NotFound from "./pages/not-found";
7
7
 
8
8
  export const router = createBrowserRouter([
9
9
  {
@@ -13,9 +13,9 @@ export const router = createBrowserRouter([
13
13
  children: [
14
14
  { index: true, Component: Home },
15
15
  { path: "about", Component: About },
16
- { path: "*", Component: NotFound },
17
16
  ],
18
17
  },
18
+ { path: "*", Component: NotFound },
19
19
  ]);
20
20
 
21
21
  export default router;
@@ -1,8 +1,9 @@
1
1
  /// <reference types="vite/client" />
2
2
 
3
3
  interface ImportMetaEnv {
4
+ readonly VITE_APP_NAME: string;
5
+ readonly VITE_APP_URL: string;
4
6
  readonly VITE_API_URL: string;
5
- readonly VITE_APP_NAME?: string;
6
7
  readonly VITE_APP_VERSION?: string;
7
8
  }
8
9
 
@@ -4,7 +4,6 @@
4
4
  "framework": "react",
5
5
  "description": "Production-ready React 19 + Vite with TypeScript, Router, TanStack Query, and more",
6
6
  "compatibility": {
7
- "databases": ["prisma"],
8
7
  "auth": ["better-auth"]
9
8
  },
10
9
  "files": [
@@ -16,6 +16,12 @@
16
16
  "noEmit": true,
17
17
  "jsx": "react-jsx",
18
18
 
19
+ /* Path aliases */
20
+ "baseUrl": ".",
21
+ "paths": {
22
+ "@/*": ["src/*"]
23
+ },
24
+
19
25
  /* Linting */
20
26
  "strict": true,
21
27
  "noUnusedLocals": true,
@@ -1,4 +1,10 @@
1
1
  {
2
2
  "files": [],
3
- "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
3
+ "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
4
+ "compilerOptions": {
5
+ "baseUrl": "./",
6
+ "paths": {
7
+ "@/*": ["src/*"]
8
+ }
9
+ }
4
10
  }
@@ -1,7 +1,19 @@
1
1
  import tailwindcss from "@tailwindcss/vite";
2
2
  import react from "@vitejs/plugin-react";
3
+ import path from "path";
3
4
  import { defineConfig } from "vite";
4
5
 
5
6
  export default defineConfig({
6
7
  plugins: [react(), tailwindcss()],
8
+ server: {
9
+ port: 3000,
10
+ },
11
+ preview: {
12
+ port: 3000,
13
+ },
14
+ resolve: {
15
+ alias: {
16
+ "@": path.resolve(__dirname, "./src"),
17
+ },
18
+ },
7
19
  });
@@ -1,3 +0,0 @@
1
- import { handlers } from "@/auth"
2
-
3
- export const { GET, POST } = handlers
@@ -1 +0,0 @@
1
- export { auth as proxy } from "@/lib/auth";
@@ -1,119 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { PrismaAdapter } from "@auth/prisma-adapter";
3
- import bcrypt from "bcryptjs";
4
- import NextAuth from "next-auth";
5
- import Credentials from "next-auth/providers/credentials";
6
- import EmailProvider from "next-auth/providers/email";
7
- import Google from "next-auth/providers/google";
8
- import {
9
- getUserById,
10
- getUserByProviderAccountId,
11
- getUserByUsername,
12
- } from "./actions/auth";
13
- import { sendEmail } from "./email/email-service";
14
- import { getVerificationEmailTemplate } from "./email/email-templates";
15
- import { prisma } from "./prisma";
16
-
17
- const options: any = {
18
- adapter: PrismaAdapter(prisma),
19
- providers: [
20
- Google({
21
- clientId: process.env.GOOGLE_CLIENT_ID ?? "",
22
- clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
23
- profile(profile: any) {
24
- const p = profile as Record<string, any>;
25
- return {
26
- id: p.sub ?? p.id,
27
- name: p.name,
28
- email: p.email,
29
- image: p.picture,
30
- role: p.role ?? "USER",
31
- };
32
- },
33
- }),
34
- (Credentials({
35
- id: "credentials",
36
- name: "Credentials",
37
- credentials: {
38
- username: { label: "Email", type: "text" },
39
- password: { label: "Password", type: "password" },
40
- },
41
- async authorize(credentials: any) {
42
- if (!credentials?.username || !credentials?.password) return null;
43
- const user = await getUserByUsername(credentials.username as string);
44
- if (!user) return null;
45
- const hashed = (user as any).password;
46
- if (!hashed) return null; // OAuth-only account
47
- const ok = await bcrypt.compare(credentials.password as string, hashed);
48
- if (!ok) return null;
49
- // strip sensitive fields
50
- const safe = { ...user } as Record<string, any>;
51
- delete safe.password;
52
- return safe;
53
- },
54
- }),
55
- EmailProvider({
56
- server: process.env.EMAIL_SERVER,
57
- from: process.env.EMAIL_FROM,
58
- async sendVerificationRequest({ identifier, url, provider }: any) {
59
- const { html, text } = getVerificationEmailTemplate(
60
- { email: identifier },
61
- url,
62
- );
63
- await sendEmail({
64
- to: identifier,
65
- subject: "Verify your email",
66
- html,
67
- text,
68
- from: provider?.from,
69
- });
70
- },
71
- })),
72
- ],
73
- callbacks: {
74
- // ensure provider-account linking is safe
75
- async signIn({ user, account }: any) {
76
- if (account && account.provider !== "credentials") {
77
- const linked = await getUserByProviderAccountId(
78
- account.providerAccountId as string,
79
- );
80
- if (linked && linked.id !== user.id)
81
- return "/sign-in/?error=OAuthAccountNotLinked";
82
- }
83
- return true;
84
- },
85
- // keep session payload minimal
86
- async jwt({ token, user }: any) {
87
- if (user) {
88
- token.user = {
89
- id: user.id,
90
- email: user.email,
91
- name: user.name,
92
- role: user.role ?? "USER",
93
- };
94
- token.role = token.user.role;
95
- }
96
- if (token.sub && !token.user) {
97
- const dbUser = await getUserById(token.sub as string);
98
- if (dbUser)
99
- token.user = {
100
- id: dbUser.id,
101
- email: dbUser.email,
102
- name: dbUser.name,
103
- role: (dbUser as any).role ?? "USER",
104
- };
105
- }
106
- return token;
107
- },
108
- async session({ session, token }: any) {
109
- session.user = token.user;
110
- session.role = token.role ?? "USER";
111
- return session;
112
- },
113
- },
114
- session: { strategy: "jwt" },
115
- secret: process.env.AUTH_SECRET,
116
- debug: process.env.NODE_ENV !== "production",
117
- };
118
-
119
- export const { handlers, signIn, signOut, auth } = NextAuth(options);