stackkit-cli 0.4.2 → 0.4.4
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/README.md +17 -10
- package/bin/stackkit.js +10 -1
- package/dist/commands/add.js +164 -25
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +34 -29
- package/dist/commands/list.js +12 -12
- package/dist/index.js +25 -23
- package/dist/types/index.d.ts +28 -16
- package/dist/utils/code-inject.d.ts +1 -1
- package/dist/utils/code-inject.js +6 -6
- package/dist/utils/detect.d.ts +1 -1
- package/dist/utils/detect.js +48 -44
- package/dist/utils/env-editor.js +20 -20
- package/dist/utils/files.js +4 -4
- package/dist/utils/json-editor.d.ts +3 -3
- package/dist/utils/json-editor.js +10 -14
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.js +8 -8
- package/dist/utils/package-manager.d.ts +2 -2
- package/dist/utils/package-manager.js +33 -26
- package/modules/auth/better-auth/files/api/auth/[...all]/route.ts +4 -0
- package/modules/auth/better-auth/files/lib/auth.ts +13 -0
- package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
- package/modules/auth/better-auth/module.json +54 -0
- package/modules/auth/{clerk-express/files/lib → clerk/files/express}/auth.ts +1 -1
- package/modules/auth/{clerk-nextjs/files/lib → clerk/files/nextjs}/auth-provider.tsx +1 -1
- package/modules/auth/clerk/files/nextjs/middleware.ts +9 -0
- package/modules/auth/{clerk-react/files/lib → clerk/files/react}/auth-provider.tsx +2 -2
- package/modules/auth/clerk/module.json +115 -0
- package/modules/database/mongoose-mongodb/files/lib/db.ts +45 -7
- package/modules/database/mongoose-mongodb/files/models/User.ts +39 -0
- package/modules/database/mongoose-mongodb/module.json +59 -7
- package/modules/database/prisma/files/lib/prisma.ts +6 -0
- package/modules/database/prisma/files/prisma/schema.prisma +8 -0
- package/modules/database/prisma/files/prisma.config.ts +12 -0
- package/modules/database/prisma/module.json +140 -0
- package/package.json +1 -1
- package/templates/express/.env.example +3 -0
- package/templates/express/eslint.config.cjs +42 -0
- package/templates/express/package.json +33 -0
- package/templates/express/src/app.ts +51 -0
- package/templates/express/src/config/env.ts +12 -0
- package/templates/express/src/features/auth/auth.controller.ts +48 -0
- package/templates/express/src/features/auth/auth.route.ts +10 -0
- package/templates/express/src/features/auth/auth.service.ts +21 -0
- package/templates/express/src/middlewares/error.middleware.ts +18 -0
- package/templates/{bases/express-base → express}/src/server.ts +3 -3
- package/templates/express/template.json +40 -0
- package/templates/express/tsconfig.json +30 -0
- package/templates/{bases/nextjs-base → nextjs}/app/layout.tsx +1 -5
- package/templates/nextjs/app/page.tsx +57 -0
- package/templates/{bases/nextjs-base → nextjs}/package.json +2 -1
- package/templates/{bases/nextjs-base → nextjs}/template.json +13 -1
- package/templates/react-vite/.env.example +2 -0
- package/templates/react-vite/README.md +85 -0
- package/templates/react-vite/eslint.config.js +23 -0
- package/templates/{bases/react-vite-base → react-vite}/index.html +1 -0
- package/templates/{bases/react-vite-base → react-vite}/package.json +16 -2
- package/templates/react-vite/src/api/client.ts +47 -0
- package/templates/react-vite/src/api/services/user.service.ts +18 -0
- package/templates/react-vite/src/components/ErrorBoundary.tsx +51 -0
- package/templates/react-vite/src/components/Layout.tsx +13 -0
- package/templates/react-vite/src/components/Loading.tsx +8 -0
- package/templates/react-vite/src/components/SEO.tsx +49 -0
- package/templates/react-vite/src/config/constants.ts +5 -0
- package/templates/react-vite/src/hooks/index.ts +64 -0
- package/templates/react-vite/src/index.css +1 -0
- package/templates/react-vite/src/lib/queryClient.ts +12 -0
- package/templates/react-vite/src/main.tsx +22 -0
- package/templates/react-vite/src/pages/About.tsx +78 -0
- package/templates/react-vite/src/pages/Home.tsx +49 -0
- package/templates/react-vite/src/pages/NotFound.tsx +24 -0
- package/templates/react-vite/src/pages/UserProfile.tsx +40 -0
- package/templates/react-vite/src/router.tsx +33 -0
- package/templates/react-vite/src/types/api.d.ts +20 -0
- package/templates/react-vite/src/types/user.d.ts +6 -0
- package/templates/react-vite/src/utils/helpers.ts +51 -0
- package/templates/react-vite/src/utils/storage.ts +35 -0
- package/templates/react-vite/src/vite-env.d.ts +11 -0
- package/templates/react-vite/template.json +46 -0
- package/templates/react-vite/tsconfig.json +4 -0
- package/templates/react-vite/vite.config.ts +13 -0
- package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
- package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
- package/modules/auth/better-auth-express/module.json +0 -38
- package/modules/auth/better-auth-nextjs/files/api/auth/[...all]/route.ts +0 -5
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
- package/modules/auth/better-auth-nextjs/module.json +0 -41
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
- package/modules/auth/better-auth-react/module.json +0 -26
- package/modules/auth/clerk-express/module.json +0 -20
- package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
- package/modules/auth/clerk-nextjs/module.json +0 -28
- package/modules/auth/clerk-react/module.json +0 -19
- package/modules/database/drizzle-postgresql/files/drizzle.config.ts +0 -10
- package/modules/database/drizzle-postgresql/files/lib/db.ts +0 -7
- package/modules/database/drizzle-postgresql/files/lib/schema.ts +0 -8
- package/modules/database/drizzle-postgresql/module.json +0 -35
- package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
- package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-mongodb/module.json +0 -36
- package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
- package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-postgresql/module.json +0 -36
- package/templates/bases/express-base/.env.example +0 -2
- package/templates/bases/express-base/package.json +0 -23
- package/templates/bases/express-base/src/app.ts +0 -34
- package/templates/bases/express-base/src/config/env.ts +0 -14
- package/templates/bases/express-base/src/middlewares/error.middleware.ts +0 -12
- package/templates/bases/express-base/template.json +0 -7
- package/templates/bases/express-base/tsconfig.json +0 -14
- package/templates/bases/nextjs-base/app/page.tsx +0 -65
- package/templates/bases/react-vite-base/README.md +0 -73
- package/templates/bases/react-vite-base/eslint.config.js +0 -23
- package/templates/bases/react-vite-base/src/App.css +0 -42
- package/templates/bases/react-vite-base/src/App.tsx +0 -35
- package/templates/bases/react-vite-base/src/index.css +0 -68
- package/templates/bases/react-vite-base/src/main.tsx +0 -10
- package/templates/bases/react-vite-base/template.json +0 -19
- package/templates/bases/react-vite-base/tsconfig.json +0 -7
- package/templates/bases/react-vite-base/vite.config.ts +0 -7
- /package/templates/{bases/nextjs-base → nextjs}/README.md +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/app/favicon.ico +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/app/globals.css +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/eslint.config.mjs +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/next.config.ts +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/postcss.config.mjs +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/file.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/globe.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/next.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/vercel.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/window.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/tsconfig.json +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/public/vite.svg +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/src/assets/react.svg +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/tsconfig.app.json +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/tsconfig.node.json +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module.exports = [
|
|
2
|
+
// Global ignores
|
|
3
|
+
{
|
|
4
|
+
ignores: ['**/node_modules/**', '**/dist/**', '.env'],
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
// TypeScript files
|
|
8
|
+
{
|
|
9
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
10
|
+
languageOptions: {
|
|
11
|
+
parser: require('@typescript-eslint/parser'),
|
|
12
|
+
parserOptions: {
|
|
13
|
+
project: './tsconfig.json',
|
|
14
|
+
ecmaVersion: 'latest',
|
|
15
|
+
sourceType: 'module',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
plugins: {
|
|
19
|
+
'@typescript-eslint': require('@typescript-eslint/eslint-plugin'),
|
|
20
|
+
},
|
|
21
|
+
rules: {
|
|
22
|
+
'prefer-const': 'error',
|
|
23
|
+
'no-console': 'off',
|
|
24
|
+
eqeqeq: ['error', 'always'],
|
|
25
|
+
curly: ['error', 'multi-line'],
|
|
26
|
+
'no-implicit-coercion': ['warn', { allow: ['!!'] }],
|
|
27
|
+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^(?:_|next)$' }],
|
|
28
|
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
29
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// JavaScript files
|
|
34
|
+
{
|
|
35
|
+
files: ['**/*.js'],
|
|
36
|
+
languageOptions: {
|
|
37
|
+
ecmaVersion: 'latest',
|
|
38
|
+
sourceType: 'module',
|
|
39
|
+
},
|
|
40
|
+
rules: {},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-express-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"lint": "eslint src --ext .ts",
|
|
10
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
11
|
+
"start": "node dist/server.js",
|
|
12
|
+
"start:prod": "cross-env NODE_ENV=production node dist/server.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"cors": "^2.8.5",
|
|
16
|
+
"dotenv": "^17.2.3",
|
|
17
|
+
"express": "^5.2.1",
|
|
18
|
+
"helmet": "^8.1.0",
|
|
19
|
+
"morgan": "^1.10.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/cors": "^2.8.19",
|
|
23
|
+
"@types/express": "^5.0.6",
|
|
24
|
+
"@types/morgan": "^1.9.10",
|
|
25
|
+
"@types/node": "^25.0.6",
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
27
|
+
"@typescript-eslint/parser": "^8.52.0",
|
|
28
|
+
"cross-env": "^10.1.0",
|
|
29
|
+
"eslint": "^9.39.2",
|
|
30
|
+
"tsx": "^4.21.0",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import cors from "cors";
|
|
2
|
+
import express, { Application, NextFunction, Request, Response } from "express";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import morgan from "morgan";
|
|
5
|
+
import { env } from "./config/env";
|
|
6
|
+
import { authRoutes } from "./features/auth/auth.route";
|
|
7
|
+
import { errorHandler } from "./middlewares/error.middleware";
|
|
8
|
+
|
|
9
|
+
// app initialization
|
|
10
|
+
const app: Application = express();
|
|
11
|
+
app.use(express.json());
|
|
12
|
+
|
|
13
|
+
// security headers
|
|
14
|
+
app.use(helmet());
|
|
15
|
+
|
|
16
|
+
// logging
|
|
17
|
+
if (env.isProduction) {
|
|
18
|
+
app.use(morgan("combined"));
|
|
19
|
+
} else {
|
|
20
|
+
app.use(morgan("dev"));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// cors configuration
|
|
24
|
+
app.use(cors());
|
|
25
|
+
|
|
26
|
+
// Home page route
|
|
27
|
+
app.get("/", (_req: Request, res: Response) => {
|
|
28
|
+
res.status(200).json({
|
|
29
|
+
title: "Welcome to your Express app",
|
|
30
|
+
description:
|
|
31
|
+
"Built with StackKit - A production-ready Express template with TypeScript, security, and best practices.",
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
docs: "https://github.com/tariqul420/stackkit",
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// routes
|
|
38
|
+
app.use("/api/auth", authRoutes);
|
|
39
|
+
|
|
40
|
+
// unhandled routes
|
|
41
|
+
app.use((req: Request, _res: Response, next: NextFunction) => {
|
|
42
|
+
const error: any = new Error(`Can't find ${req.originalUrl} on this server!`);
|
|
43
|
+
error.status = 404;
|
|
44
|
+
|
|
45
|
+
next(error);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Global error handler
|
|
49
|
+
app.use(errorHandler);
|
|
50
|
+
|
|
51
|
+
export default app;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
5
|
+
|
|
6
|
+
const env = {
|
|
7
|
+
port: Number(process.env.PORT) || 3000,
|
|
8
|
+
node_env: process.env.NODE_ENV || "development",
|
|
9
|
+
isProduction: (process.env.NODE_ENV || "development") === "production",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { env };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { authServices } from "./auth.service";
|
|
3
|
+
|
|
4
|
+
const signup = async (req: Request, res: Response, next: NextFunction) => {
|
|
5
|
+
try {
|
|
6
|
+
const result = await authServices.signup(req.body);
|
|
7
|
+
|
|
8
|
+
res.status(201).json({
|
|
9
|
+
success: true,
|
|
10
|
+
message: "User registered successfully",
|
|
11
|
+
data: result,
|
|
12
|
+
});
|
|
13
|
+
} catch (error) {
|
|
14
|
+
next(error);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const signin = async (req: Request, res: Response, next: NextFunction) => {
|
|
19
|
+
try {
|
|
20
|
+
const { email, password } = req.body;
|
|
21
|
+
|
|
22
|
+
const result = await authServices.signin(email, password);
|
|
23
|
+
|
|
24
|
+
if (!result) {
|
|
25
|
+
res.status(401).json({
|
|
26
|
+
success: false,
|
|
27
|
+
message: "Invalid email or password",
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
res.status(200).json({
|
|
33
|
+
success: true,
|
|
34
|
+
message: "Login successful",
|
|
35
|
+
data: {
|
|
36
|
+
token: result.token,
|
|
37
|
+
user: result.user,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
next(error);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const authController = {
|
|
46
|
+
signup,
|
|
47
|
+
signin,
|
|
48
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { authController } from "./auth.controller";
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
// routes
|
|
7
|
+
router.post("/signup", authController.signup);
|
|
8
|
+
router.post("/signin", authController.signin);
|
|
9
|
+
|
|
10
|
+
export const authRoutes = router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
|
+
|
|
4
|
+
const signup = async (payload: Record<string, unknown>) => {
|
|
5
|
+
// your logic here
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const signin = async (
|
|
9
|
+
email: string,
|
|
10
|
+
password: string,
|
|
11
|
+
): Promise<{ token: string; user: any } | null> => {
|
|
12
|
+
// your logic here
|
|
13
|
+
return null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const authServices = {
|
|
17
|
+
signup,
|
|
18
|
+
signin,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type { User };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { env } from "../config/env";
|
|
3
|
+
|
|
4
|
+
export const errorHandler = (err: any, _req: Request, res: Response, _: NextFunction) => {
|
|
5
|
+
const statusCode = err.status || 500;
|
|
6
|
+
const errorMessage = err?.message || "Internal server error!";
|
|
7
|
+
|
|
8
|
+
const payload: any = {
|
|
9
|
+
success: false,
|
|
10
|
+
message: errorMessage,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (!env.isProduction) {
|
|
14
|
+
payload.errors = err?.stack || err;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
res.status(statusCode).json(payload);
|
|
18
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import app from
|
|
2
|
-
import { env } from
|
|
1
|
+
import app from "./app";
|
|
2
|
+
import { env } from "./config/env";
|
|
3
3
|
|
|
4
|
-
const port = env.
|
|
4
|
+
const port = env.port;
|
|
5
5
|
|
|
6
6
|
app.listen(port, () => {
|
|
7
7
|
console.log(`Server is running on http://localhost:${port}`);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "express-base",
|
|
3
|
+
"displayName": "Express.js",
|
|
4
|
+
"framework": "express",
|
|
5
|
+
"description": "Express.js REST API with TypeScript",
|
|
6
|
+
"files": ["src/", ".gitignore", "package.json", "tsconfig.json", ".env.example"],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "tsx watch src/server.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"lint": "eslint src --ext .ts",
|
|
11
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
12
|
+
"start": "node dist/server.js",
|
|
13
|
+
"start:prod": "cross-env NODE_ENV=production node dist/server.js"
|
|
14
|
+
},
|
|
15
|
+
"jsScripts": {
|
|
16
|
+
"dev": "tsx --watch src/server.js",
|
|
17
|
+
"build": "echo 'No build step for JavaScript'",
|
|
18
|
+
"lint": "eslint src --ext .js",
|
|
19
|
+
"lint:fix": "eslint src --ext .js --fix",
|
|
20
|
+
"start": "node src/server.js",
|
|
21
|
+
"start:prod": "cross-env NODE_ENV=production node src/server.js"
|
|
22
|
+
},
|
|
23
|
+
"fileReplacements": [
|
|
24
|
+
{
|
|
25
|
+
"file": "src/server.js",
|
|
26
|
+
"from": "import app from './app'",
|
|
27
|
+
"to": "import app from './app.js'"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"file": "src/app.js",
|
|
31
|
+
"from": "import { env } from './config/env'",
|
|
32
|
+
"to": "import { env } from './config/env.js'"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"file": "src/app.js",
|
|
36
|
+
"from": "import { errorHandler } from './middlewares/error.middleware'",
|
|
37
|
+
"to": "import { errorHandler } from './middlewares/error.middleware.js'"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"outDir": "./dist",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"target": "ES2023",
|
|
7
|
+
"lib": ["ES2023"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"declaration": false,
|
|
16
|
+
"declarationMap": false,
|
|
17
|
+
"removeComments": false,
|
|
18
|
+
"noImplicitAny": true,
|
|
19
|
+
"noImplicitReturns": true,
|
|
20
|
+
"noImplicitThis": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"exactOptionalPropertyTypes": true,
|
|
24
|
+
"noEmitOnError": true,
|
|
25
|
+
"incremental": false,
|
|
26
|
+
"ignoreDeprecations": "5.0"
|
|
27
|
+
},
|
|
28
|
+
"include": ["src/**/*"],
|
|
29
|
+
"exclude": ["node_modules", "dist"]
|
|
30
|
+
}
|
|
@@ -24,11 +24,7 @@ export default function RootLayout({
|
|
|
24
24
|
}>) {
|
|
25
25
|
return (
|
|
26
26
|
<html lang="en">
|
|
27
|
-
<body
|
|
28
|
-
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
29
|
-
>
|
|
30
|
-
{children}
|
|
31
|
-
</body>
|
|
27
|
+
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>{children}</body>
|
|
32
28
|
</html>
|
|
33
29
|
);
|
|
34
30
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Image from "next/image";
|
|
2
|
+
|
|
3
|
+
export default function Home() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
|
6
|
+
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
|
7
|
+
<div className="flex items-center gap-4 mb-8">
|
|
8
|
+
<div className="text-2xl font-bold text-black dark:text-white">Stackkit</div>
|
|
9
|
+
<span className="text-xl text-zinc-400">+</span>
|
|
10
|
+
<Image
|
|
11
|
+
className="dark:invert"
|
|
12
|
+
src="/next.svg"
|
|
13
|
+
alt="Next.js logo"
|
|
14
|
+
width={100}
|
|
15
|
+
height={20}
|
|
16
|
+
priority
|
|
17
|
+
/>
|
|
18
|
+
</div>
|
|
19
|
+
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
|
20
|
+
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
|
21
|
+
To get started, edit the page.tsx file.
|
|
22
|
+
</h1>
|
|
23
|
+
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
|
24
|
+
This template includes Next.js, Tailwind CSS, and Stackkit best practices. Check out the{" "}
|
|
25
|
+
<a
|
|
26
|
+
href="https://github.com/tariqul420/stackkit"
|
|
27
|
+
className="font-medium text-zinc-950 dark:text-zinc-50 hover:underline"
|
|
28
|
+
target="_blank"
|
|
29
|
+
rel="noopener noreferrer"
|
|
30
|
+
>
|
|
31
|
+
Stackkit repository
|
|
32
|
+
</a>{" "}
|
|
33
|
+
for more info.
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
|
37
|
+
<a
|
|
38
|
+
className="flex h-12 w-full items-center justify-center rounded-full bg-white text-black px-5 transition-colors hover:bg-zinc-200 md:w-40"
|
|
39
|
+
href="https://nextjs.org/docs"
|
|
40
|
+
target="_blank"
|
|
41
|
+
rel="noopener noreferrer"
|
|
42
|
+
>
|
|
43
|
+
Documentation
|
|
44
|
+
</a>
|
|
45
|
+
<a
|
|
46
|
+
className="flex h-12 w-full items-center justify-center rounded-full bg-black text-white px-5 transition-colors hover:bg-zinc-900 md:w-40"
|
|
47
|
+
href="https://github.com/tariqul420/stackkit"
|
|
48
|
+
target="_blank"
|
|
49
|
+
rel="noopener noreferrer"
|
|
50
|
+
>
|
|
51
|
+
Stackkit GitHub
|
|
52
|
+
</a>
|
|
53
|
+
</div>
|
|
54
|
+
</main>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -14,5 +14,17 @@
|
|
|
14
14
|
"postcss.config.mjs",
|
|
15
15
|
"README.md",
|
|
16
16
|
"tsconfig.json"
|
|
17
|
-
]
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "next dev",
|
|
20
|
+
"build": "next build",
|
|
21
|
+
"start": "next start",
|
|
22
|
+
"lint": "eslint"
|
|
23
|
+
},
|
|
24
|
+
"jsScripts": {
|
|
25
|
+
"dev": "next dev",
|
|
26
|
+
"build": "next build",
|
|
27
|
+
"start": "next start",
|
|
28
|
+
"lint": "eslint"
|
|
29
|
+
}
|
|
18
30
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# React + Vite Template
|
|
2
|
+
|
|
3
|
+
A production-ready React starter template with TypeScript, Vite, and essential libraries pre-configured.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **React 19** with TypeScript
|
|
8
|
+
- **Vite 7** for fast development
|
|
9
|
+
- **React Router v7** for client-side routing
|
|
10
|
+
- **TanStack Query v5** for data fetching and caching
|
|
11
|
+
- **Axios** with interceptors
|
|
12
|
+
- **Tailwind CSS** for styling
|
|
13
|
+
- **React Hot Toast** for notifications
|
|
14
|
+
- **SEO Ready** with React Helmet Async
|
|
15
|
+
- **Error Boundaries** for graceful error handling
|
|
16
|
+
- **ESLint** for code quality
|
|
17
|
+
- **Custom Hooks** included
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install dependencies
|
|
23
|
+
pnpm install
|
|
24
|
+
|
|
25
|
+
# Start development server
|
|
26
|
+
pnpm dev
|
|
27
|
+
|
|
28
|
+
# Build for production
|
|
29
|
+
pnpm build
|
|
30
|
+
|
|
31
|
+
# Preview production build
|
|
32
|
+
pnpm preview
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Project Structure
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
src/
|
|
39
|
+
├── api/ # API client & interceptors
|
|
40
|
+
├── components/ # Reusable UI components
|
|
41
|
+
├── config/ # App configuration
|
|
42
|
+
├── hooks/ # Custom React hooks
|
|
43
|
+
├── lib/ # Library configurations
|
|
44
|
+
├── pages/ # Route pages
|
|
45
|
+
├── types/ # TypeScript types
|
|
46
|
+
├── utils/ # Helper functions
|
|
47
|
+
├── App.tsx # Main app component
|
|
48
|
+
├── main.tsx # Entry point
|
|
49
|
+
└── index.css # Global styles
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Environment Variables
|
|
53
|
+
|
|
54
|
+
Create a `.env` file:
|
|
55
|
+
|
|
56
|
+
```env
|
|
57
|
+
VITE_API_URL=http://localhost:3000/api
|
|
58
|
+
VITE_APP_NAME=My App
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Production Build
|
|
62
|
+
|
|
63
|
+
- Code splitting with vendor chunks
|
|
64
|
+
- Tree shaking to remove unused code
|
|
65
|
+
- Minified and compressed output
|
|
66
|
+
- TypeScript strict mode enabled
|
|
67
|
+
- ESLint configured for code quality
|
|
68
|
+
|
|
69
|
+
## Deployment
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Deploy the `dist` folder to Vercel, Netlify, or any static hosting service.
|
|
76
|
+
|
|
77
|
+
## Tech Stack
|
|
78
|
+
|
|
79
|
+
- React 19 - UI library
|
|
80
|
+
- Vite 7 - Build tool
|
|
81
|
+
- TypeScript - Type safety
|
|
82
|
+
- React Router v7 - Routing
|
|
83
|
+
- TanStack Query v5 - Data fetching
|
|
84
|
+
- Axios - HTTP client
|
|
85
|
+
- Tailwind CSS - Styling
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
+
import reactRefresh from "eslint-plugin-react-refresh";
|
|
5
|
+
import tseslint from "typescript-eslint";
|
|
6
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
globalIgnores(["dist"]),
|
|
10
|
+
{
|
|
11
|
+
files: ["**/*.{ts,tsx}"],
|
|
12
|
+
extends: [
|
|
13
|
+
js.configs.recommended,
|
|
14
|
+
tseslint.configs.recommended,
|
|
15
|
+
reactHooks.configs.flat.recommended,
|
|
16
|
+
reactRefresh.configs.vite,
|
|
17
|
+
],
|
|
18
|
+
languageOptions: {
|
|
19
|
+
ecmaVersion: 2020,
|
|
20
|
+
globals: globals.browser,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
]);
|
|
@@ -7,11 +7,22 @@
|
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "tsc -b && vite build",
|
|
9
9
|
"lint": "eslint .",
|
|
10
|
+
"lint:fix": "eslint . --fix",
|
|
10
11
|
"preview": "vite preview"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
14
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
15
|
+
"@tanstack/react-query": "^5.62.0",
|
|
16
|
+
"@tanstack/react-query-devtools": "^5.62.0",
|
|
17
|
+
"axios": "^1.7.7",
|
|
18
|
+
"class-variance-authority": "^0.7.1",
|
|
19
|
+
"clsx": "^2.1.1",
|
|
13
20
|
"react": "^19.2.0",
|
|
14
|
-
"react-dom": "^19.2.0"
|
|
21
|
+
"react-dom": "^19.2.0",
|
|
22
|
+
"react-helmet-async": "^2.0.5",
|
|
23
|
+
"react-hot-toast": "^2.4.1",
|
|
24
|
+
"react-router": "^7.12.0",
|
|
25
|
+
"tailwind-merge": "^3.4.0"
|
|
15
26
|
},
|
|
16
27
|
"devDependencies": {
|
|
17
28
|
"@eslint/js": "^9.39.1",
|
|
@@ -19,12 +30,15 @@
|
|
|
19
30
|
"@types/react": "^19.2.5",
|
|
20
31
|
"@types/react-dom": "^19.2.3",
|
|
21
32
|
"@vitejs/plugin-react": "^5.1.1",
|
|
33
|
+
"autoprefixer": "^10.4.23",
|
|
22
34
|
"eslint": "^9.39.1",
|
|
23
35
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
24
36
|
"eslint-plugin-react-refresh": "^0.4.24",
|
|
25
37
|
"globals": "^16.5.0",
|
|
38
|
+
"postcss": "^8.5.6",
|
|
39
|
+
"tailwindcss": "^4.1.18",
|
|
26
40
|
"typescript": "~5.9.3",
|
|
27
41
|
"typescript-eslint": "^8.46.4",
|
|
28
42
|
"vite": "^7.2.4"
|
|
29
43
|
}
|
|
30
|
-
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AxiosResponse } from "axios";
|
|
2
|
+
import axios, { AxiosError } from "axios";
|
|
3
|
+
import toast from "react-hot-toast";
|
|
4
|
+
|
|
5
|
+
const api = axios.create({
|
|
6
|
+
baseURL: import.meta.env.VITE_API_URL || "http://localhost:3000/api",
|
|
7
|
+
timeout: 10000,
|
|
8
|
+
headers: {
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
api.interceptors.request.use(
|
|
14
|
+
(config) => {
|
|
15
|
+
const token = localStorage.getItem("auth_token");
|
|
16
|
+
if (token) {
|
|
17
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
18
|
+
}
|
|
19
|
+
return config;
|
|
20
|
+
},
|
|
21
|
+
(error: AxiosError) => {
|
|
22
|
+
return Promise.reject(error);
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
api.interceptors.response.use(
|
|
27
|
+
(response: AxiosResponse) => response,
|
|
28
|
+
(error: AxiosError) => {
|
|
29
|
+
if (error.response?.status === 401) {
|
|
30
|
+
localStorage.removeItem("auth_token");
|
|
31
|
+
toast.error("Session expired. Please login again.");
|
|
32
|
+
} else if (error.response?.status === 403) {
|
|
33
|
+
toast.error("You do not have permission to perform this action.");
|
|
34
|
+
} else if (error.response?.status === 404) {
|
|
35
|
+
toast.error("Resource not found.");
|
|
36
|
+
} else if (error.response?.status === 500) {
|
|
37
|
+
toast.error("Server error. Please try again later.");
|
|
38
|
+
} else if (error.code === "ECONNABORTED") {
|
|
39
|
+
toast.error("Request timeout. Please try again.");
|
|
40
|
+
} else if (!error.response) {
|
|
41
|
+
toast.error("Network error. Please check your connection.");
|
|
42
|
+
}
|
|
43
|
+
return Promise.reject(error);
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
export default api;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import api from "../client";
|
|
2
|
+
|
|
3
|
+
export const userService = {
|
|
4
|
+
getUser: async (id: string): Promise<User> => {
|
|
5
|
+
const response = await api.get<ApiResponse<User>>(`/users/${id}`);
|
|
6
|
+
return response.data.data;
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
getCurrentUser: async (): Promise<User> => {
|
|
10
|
+
const response = await api.get<ApiResponse<User>>("/users/me");
|
|
11
|
+
return response.data.data;
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
updateUser: async (id: string, data: Partial<User>): Promise<User> => {
|
|
15
|
+
const response = await api.patch<ApiResponse<User>>(`/users/${id}`, data);
|
|
16
|
+
return response.data.data;
|
|
17
|
+
},
|
|
18
|
+
};
|