stackkit-cli 0.4.3 → 0.4.5
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 +26 -2
- package/bin/stackkit.js +10 -1
- package/dist/commands/add.js +128 -10
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +483 -0
- package/dist/commands/list.d.ts +1 -1
- package/dist/commands/list.js +59 -38
- package/dist/index.js +11 -13
- package/dist/types/index.d.ts +29 -2
- package/dist/utils/config-utils.d.ts +2 -0
- package/dist/utils/config-utils.js +88 -0
- package/dist/utils/detect.js +12 -2
- package/dist/utils/env-editor.d.ts +0 -1
- package/dist/utils/env-editor.js +50 -25
- package/dist/utils/files.d.ts +8 -0
- package/dist/utils/files.js +51 -0
- package/dist/utils/js-conversion.d.ts +1 -0
- package/dist/utils/js-conversion.js +244 -0
- package/dist/utils/module-utils.d.ts +2 -0
- package/dist/utils/module-utils.js +461 -0
- package/dist/utils/package-manager.js +15 -31
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
- package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
- package/modules/auth/authjs/files/lib/auth.ts +41 -0
- package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
- package/modules/auth/authjs/module.json +95 -0
- package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
- package/modules/auth/better-auth/files/lib/auth.ts +75 -0
- package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
- package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
- package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
- package/modules/auth/better-auth/module.json +191 -0
- package/modules/database/mongoose/files/lib/db.ts +68 -0
- package/modules/database/mongoose/files/models/User.ts +34 -0
- package/modules/database/mongoose/module.json +55 -0
- package/modules/database/prisma/files/lib/prisma.ts +4 -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 +122 -0
- package/package.json +1 -1
- package/templates/express/.env.example +1 -10
- package/templates/express/package.json +15 -21
- package/templates/express/src/app.ts +9 -29
- package/templates/express/src/config/env.ts +3 -14
- package/templates/express/src/features/health/health.controller.ts +18 -0
- package/templates/express/src/features/health/health.route.ts +9 -0
- package/templates/express/src/features/health/health.service.ts +6 -0
- package/templates/express/src/middlewares/error.middleware.ts +2 -2
- package/templates/express/src/server.ts +1 -1
- package/templates/express/template.json +1 -5
- package/templates/express/tsconfig.json +0 -1
- package/templates/nextjs/lib/env.ts +8 -0
- package/templates/nextjs/package.json +7 -7
- package/templates/react-vite/.env.example +1 -2
- package/templates/react-vite/.prettierignore +4 -0
- package/templates/react-vite/.prettierrc +9 -0
- package/templates/react-vite/README.md +22 -0
- package/templates/react-vite/package.json +16 -16
- package/templates/react-vite/src/router.tsx +0 -12
- package/templates/react-vite/vite.config.ts +0 -6
- package/dist/commands/init.d.ts +0 -10
- package/dist/commands/init.js +0 -157
- package/dist/utils/code-inject.d.ts +0 -14
- package/dist/utils/code-inject.js +0 -70
- package/dist/utils/json-editor.d.ts +0 -8
- package/dist/utils/json-editor.js +0 -45
- package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
- package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
- package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
- 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/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-express/module.json +0 -61
- package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
- package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
- package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/module.json +0 -62
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
- package/modules/auth/better-auth-react/module.json +0 -28
- package/modules/auth/clerk-express/files/lib/auth.ts +0 -7
- package/modules/auth/clerk-express/module.json +0 -34
- package/modules/auth/clerk-nextjs/files/lib/auth-provider.tsx +0 -5
- package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
- package/modules/auth/clerk-nextjs/module.json +0 -64
- package/modules/auth/clerk-react/files/lib/auth-provider.tsx +0 -15
- package/modules/auth/clerk-react/module.json +0 -28
- package/modules/database/mongoose-mongodb/files/lib/db.ts +0 -40
- package/modules/database/mongoose-mongodb/module.json +0 -55
- 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 -60
- 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 -60
- package/templates/react-vite/src/api/services/user.service.ts +0 -18
- package/templates/react-vite/src/pages/UserProfile.tsx +0 -40
- package/templates/react-vite/src/types/user.d.ts +0 -6
- /package/modules/auth/{better-auth-nextjs → better-auth}/files/api/auth/[...all]/route.ts +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mongoose",
|
|
3
|
+
"displayName": "Mongoose (MongoDB)",
|
|
4
|
+
"description": "Mongoose ODM for MongoDB database",
|
|
5
|
+
"category": "database",
|
|
6
|
+
"provider": "mongoose",
|
|
7
|
+
"database": "mongodb",
|
|
8
|
+
"supportedFrameworks": ["nextjs", "express"],
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"mongoose": "^8.8.4",
|
|
11
|
+
"mongodb": "^6.10.0"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/mongoose": "^5.11.97"
|
|
15
|
+
},
|
|
16
|
+
"envVars": {
|
|
17
|
+
"common": [
|
|
18
|
+
{
|
|
19
|
+
"key": "DATABASE_URL",
|
|
20
|
+
"value": "mongodb+srv://username:password@cluster.mongodb.net/mydb",
|
|
21
|
+
"required": true
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"frameworkPatches": {
|
|
26
|
+
"express": {
|
|
27
|
+
"tsconfig.json": {
|
|
28
|
+
"merge": {
|
|
29
|
+
"compilerOptions": {
|
|
30
|
+
"baseUrl": ".",
|
|
31
|
+
"paths": {
|
|
32
|
+
"@/*": ["./*"]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"include": ["src/**/*", "lib/**/*"],
|
|
36
|
+
"exclude": ["node_modules", "dist"]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"patches": [
|
|
42
|
+
{
|
|
43
|
+
"type": "create-file",
|
|
44
|
+
"description": "{{dbDescription}}",
|
|
45
|
+
"source": "lib/{{dbFile}}",
|
|
46
|
+
"destination": "{{lib}}/{{dbFile}}"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"type": "create-file",
|
|
50
|
+
"description": "Create sample User model",
|
|
51
|
+
"source": "models/User.ts",
|
|
52
|
+
"destination": "{{models}}/User.ts"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prisma",
|
|
3
|
+
"displayName": "Prisma",
|
|
4
|
+
"description": "Prisma ORM",
|
|
5
|
+
"category": "database",
|
|
6
|
+
"provider": "prisma",
|
|
7
|
+
"supportedFrameworks": ["nextjs", "express"],
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"common": {
|
|
10
|
+
"@prisma/client": "^7.2.0",
|
|
11
|
+
"dotenv": "^17.2.3"
|
|
12
|
+
},
|
|
13
|
+
"providers": {
|
|
14
|
+
"postgresql": {
|
|
15
|
+
"@prisma/adapter-pg": "^7.2.0",
|
|
16
|
+
"pg": "^8.16.3"
|
|
17
|
+
},
|
|
18
|
+
"mongodb": {
|
|
19
|
+
"@prisma/client": "6.19"
|
|
20
|
+
},
|
|
21
|
+
"mysql": {
|
|
22
|
+
"@prisma/adapter-mariadb": "^7.2.0"
|
|
23
|
+
},
|
|
24
|
+
"sqlite": {
|
|
25
|
+
"@prisma/adapter-better-sqlite3": "^7.2.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"common": {
|
|
31
|
+
"prisma": "^7.2.0",
|
|
32
|
+
"tsx": "^4.21.0"
|
|
33
|
+
},
|
|
34
|
+
"providers": {
|
|
35
|
+
"postgresql": {
|
|
36
|
+
"@types/pg": "^8.16.0"
|
|
37
|
+
},
|
|
38
|
+
"mongodb": {
|
|
39
|
+
"prisma": "6.19"
|
|
40
|
+
},
|
|
41
|
+
"mysql": {},
|
|
42
|
+
"sqlite": {
|
|
43
|
+
"@types/better-sqlite3": "^7.6.13"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"envVars": {
|
|
48
|
+
"common": [
|
|
49
|
+
{
|
|
50
|
+
"key": "DATABASE_URL",
|
|
51
|
+
"value": "{{connectionString}}",
|
|
52
|
+
"required": true
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"providers": {
|
|
56
|
+
"mysql": [
|
|
57
|
+
{
|
|
58
|
+
"key": "DATABASE_HOST",
|
|
59
|
+
"value": "localhost",
|
|
60
|
+
"required": true
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"key": "DATABASE_USER",
|
|
64
|
+
"value": "",
|
|
65
|
+
"required": true
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"key": "DATABASE_PASSWORD",
|
|
69
|
+
"value": "",
|
|
70
|
+
"required": true
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"key": "DATABASE_NAME",
|
|
74
|
+
"value": "mydb",
|
|
75
|
+
"required": true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"key": "DATABASE_PORT",
|
|
79
|
+
"value": "3306",
|
|
80
|
+
"required": false
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"frameworkPatches": {
|
|
86
|
+
"express": {
|
|
87
|
+
"tsconfig.json": {
|
|
88
|
+
"merge": {
|
|
89
|
+
"compilerOptions": {
|
|
90
|
+
"baseUrl": ".",
|
|
91
|
+
"paths": {
|
|
92
|
+
"@/*": ["./*"]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"include": ["src/**/*", "lib/**/*"],
|
|
96
|
+
"exclude": ["node_modules", "dist", "lib/generated/prisma"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"patches": [
|
|
102
|
+
{
|
|
103
|
+
"type": "create-file",
|
|
104
|
+
"description": "Create Prisma schema",
|
|
105
|
+
"source": "prisma/schema.prisma",
|
|
106
|
+
"destination": "prisma/schema.prisma"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"type": "create-file",
|
|
110
|
+
"description": "Create Prisma config",
|
|
111
|
+
"source": "prisma.config.ts",
|
|
112
|
+
"destination": "prisma.config.ts"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"type": "create-file",
|
|
116
|
+
"description": "{{dbDescription}}",
|
|
117
|
+
"source": "lib/{{dbFile}}",
|
|
118
|
+
"destination": "{{lib}}/{{dbFile}}"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"postInstall": ["npx prisma generate"]
|
|
122
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
1
|
PORT=3000
|
|
2
|
-
NODE_ENV=development
|
|
3
|
-
APP_URL=http://localhost:3000
|
|
4
|
-
SITE_URL=http://localhost:5173
|
|
5
|
-
|
|
6
|
-
# Proxy (set to true if running behind a proxy/load-balancer)
|
|
7
|
-
TRUST_PROXY=false
|
|
8
|
-
|
|
9
|
-
# Rate limit
|
|
10
|
-
RATE_LIMIT_MAX=100
|
|
11
|
-
RATE_LIMIT_WINDOW_MS=900000
|
|
2
|
+
NODE_ENV=development
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "my-app",
|
|
2
|
+
"name": "my-express-app",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "tsx watch src/server.ts",
|
|
8
|
-
"clean": "rimraf dist",
|
|
9
|
-
"prebuild": "npm run clean",
|
|
10
8
|
"build": "tsc",
|
|
11
9
|
"lint": "eslint src --ext .ts",
|
|
12
10
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
@@ -14,26 +12,22 @@
|
|
|
14
12
|
"start:prod": "cross-env NODE_ENV=production node dist/server.js"
|
|
15
13
|
},
|
|
16
14
|
"dependencies": {
|
|
17
|
-
"compression": "^1.7.4",
|
|
18
15
|
"cors": "^2.8.5",
|
|
19
|
-
"dotenv": "^
|
|
20
|
-
"express": "^
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"morgan": "^1.10.0"
|
|
16
|
+
"dotenv": "^17.2.3",
|
|
17
|
+
"express": "^5.2.1",
|
|
18
|
+
"helmet": "^8.1.0",
|
|
19
|
+
"morgan": "^1.10.1"
|
|
24
20
|
},
|
|
25
21
|
"devDependencies": {
|
|
26
|
-
"@types/
|
|
27
|
-
"@types/
|
|
28
|
-
"@types/
|
|
29
|
-
"@types/
|
|
30
|
-
"@
|
|
31
|
-
"@typescript-eslint/
|
|
32
|
-
"
|
|
33
|
-
"cross-env": "^7.0.3",
|
|
22
|
+
"@types/cors": "^2.8.19",
|
|
23
|
+
"@types/express": "^5.0.6",
|
|
24
|
+
"@types/morgan": "^1.9.10",
|
|
25
|
+
"@types/node": "^25.0.8",
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
27
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
28
|
+
"cross-env": "^10.1.0",
|
|
34
29
|
"eslint": "^9.39.2",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"typescript": "^5.7.2"
|
|
30
|
+
"tsx": "^4.21.0",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
38
32
|
}
|
|
39
|
-
}
|
|
33
|
+
}
|
|
@@ -1,54 +1,31 @@
|
|
|
1
|
-
import compression from "compression";
|
|
2
1
|
import cors from "cors";
|
|
3
2
|
import express, { Application, NextFunction, Request, Response } from "express";
|
|
4
|
-
import rateLimit from "express-rate-limit";
|
|
5
3
|
import helmet from "helmet";
|
|
6
4
|
import morgan from "morgan";
|
|
7
5
|
import { env } from "./config/env";
|
|
6
|
+
import { authRoutes } from "./features/health/health.route";
|
|
8
7
|
import { errorHandler } from "./middlewares/error.middleware";
|
|
9
8
|
|
|
10
9
|
// app initialization
|
|
11
10
|
const app: Application = express();
|
|
12
11
|
app.use(express.json());
|
|
13
12
|
|
|
14
|
-
// trust proxy when behind reverse proxy (like Heroku, Vercel, Load Balancers)
|
|
15
|
-
if (env.app.trust_proxy) {
|
|
16
|
-
app.set("trust proxy", 1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
13
|
// security headers
|
|
20
14
|
app.use(helmet());
|
|
21
15
|
|
|
22
|
-
// response compression
|
|
23
|
-
app.use(compression());
|
|
24
|
-
|
|
25
|
-
// rate limiting
|
|
26
|
-
const limiter = rateLimit({
|
|
27
|
-
windowMs: env.app.rateLimit.windowMs,
|
|
28
|
-
max: env.app.rateLimit.max,
|
|
29
|
-
standardHeaders: true,
|
|
30
|
-
legacyHeaders: false,
|
|
31
|
-
});
|
|
32
|
-
app.use(limiter);
|
|
33
|
-
|
|
34
16
|
// logging
|
|
35
|
-
if (env.
|
|
17
|
+
if (env.isProduction) {
|
|
36
18
|
app.use(morgan("combined"));
|
|
37
19
|
} else {
|
|
38
20
|
app.use(morgan("dev"));
|
|
39
21
|
}
|
|
40
22
|
|
|
41
23
|
// cors configuration
|
|
42
|
-
app.use(
|
|
43
|
-
cors({
|
|
44
|
-
origin: [env.app.site_url],
|
|
45
|
-
credentials: true,
|
|
46
|
-
}),
|
|
47
|
-
);
|
|
24
|
+
app.use(cors());
|
|
48
25
|
|
|
49
26
|
// Home page route
|
|
50
|
-
app.get("/", (
|
|
51
|
-
res.json({
|
|
27
|
+
app.get("/", (_req: Request, res: Response) => {
|
|
28
|
+
res.status(200).json({
|
|
52
29
|
title: "Welcome to your Express app",
|
|
53
30
|
description:
|
|
54
31
|
"Built with StackKit - A production-ready Express template with TypeScript, security, and best practices.",
|
|
@@ -57,8 +34,11 @@ app.get("/", (req: Request, res: Response) => {
|
|
|
57
34
|
});
|
|
58
35
|
});
|
|
59
36
|
|
|
37
|
+
// routes
|
|
38
|
+
app.use("/api/health", authRoutes);
|
|
39
|
+
|
|
60
40
|
// unhandled routes
|
|
61
|
-
app.use((req: Request,
|
|
41
|
+
app.use((req: Request, _res: Response, next: NextFunction) => {
|
|
62
42
|
const error: any = new Error(`Can't find ${req.originalUrl} on this server!`);
|
|
63
43
|
error.status = 404;
|
|
64
44
|
|
|
@@ -4,20 +4,9 @@ import path from "path";
|
|
|
4
4
|
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
5
5
|
|
|
6
6
|
const env = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
site_url: process.env.SITE_URL || "http://localhost:5173",
|
|
11
|
-
trust_proxy: (process.env.TRUST_PROXY || "false") === "true",
|
|
12
|
-
rateLimit: {
|
|
13
|
-
max: Number(process.env.RATE_LIMIT_MAX) || 100,
|
|
14
|
-
windowMs: Number(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
node: {
|
|
18
|
-
env: process.env.NODE_ENV || "development",
|
|
19
|
-
isProduction: (process.env.NODE_ENV || "development") === "production",
|
|
20
|
-
},
|
|
7
|
+
port: Number(process.env.PORT) || 3000,
|
|
8
|
+
node_env: process.env.NODE_ENV || "development",
|
|
9
|
+
isProduction: (process.env.NODE_ENV || "development") === "production",
|
|
21
10
|
};
|
|
22
11
|
|
|
23
12
|
export { env };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
|
|
3
|
+
const health = async (_req: Request, res: Response, next: NextFunction) => {
|
|
4
|
+
try {
|
|
5
|
+
res.status(200).json({
|
|
6
|
+
success: true,
|
|
7
|
+
message: "API is healthy!",
|
|
8
|
+
timestamp: new Date().toISOString(),
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
});
|
|
11
|
+
} catch (error) {
|
|
12
|
+
next(error);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const healthController = {
|
|
17
|
+
health,
|
|
18
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextFunction, Request, Response } from "express";
|
|
2
2
|
import { env } from "../config/env";
|
|
3
3
|
|
|
4
|
-
export const errorHandler = (err: any,
|
|
4
|
+
export const errorHandler = (err: any, _req: Request, res: Response, _: NextFunction) => {
|
|
5
5
|
const statusCode = err.status || 500;
|
|
6
6
|
const errorMessage = err?.message || "Internal server error!";
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ export const errorHandler = (err: any, req: Request, res: Response, next: NextFu
|
|
|
10
10
|
message: errorMessage,
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
if (!env.
|
|
13
|
+
if (!env.isProduction) {
|
|
14
14
|
payload.errors = err?.stack || err;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
"displayName": "Express.js",
|
|
4
4
|
"framework": "express",
|
|
5
5
|
"description": "Express.js REST API with TypeScript",
|
|
6
|
-
"files": ["src/", ".
|
|
6
|
+
"files": ["src/", ".gitignore", "package.json", "tsconfig.json", ".env.example"],
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "tsx watch src/server.ts",
|
|
9
|
-
"clean": "rimraf dist",
|
|
10
|
-
"prebuild": "npm run clean",
|
|
11
9
|
"build": "tsc",
|
|
12
10
|
"lint": "eslint src --ext .ts",
|
|
13
11
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
@@ -16,8 +14,6 @@
|
|
|
16
14
|
},
|
|
17
15
|
"jsScripts": {
|
|
18
16
|
"dev": "tsx --watch src/server.js",
|
|
19
|
-
"clean": "rimraf dist",
|
|
20
|
-
"prebuild": "npm run clean",
|
|
21
17
|
"build": "echo 'No build step for JavaScript'",
|
|
22
18
|
"lint": "eslint src --ext .js",
|
|
23
19
|
"lint:fix": "eslint src --ext .js --fix",
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"react-dom": "19.2.3"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@tailwindcss/postcss": "^4",
|
|
19
|
-
"@types/node": "^
|
|
20
|
-
"@types/react": "^19",
|
|
21
|
-
"@types/react-dom": "^19",
|
|
22
|
-
"eslint": "^9",
|
|
18
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
19
|
+
"@types/node": "^25.0.8",
|
|
20
|
+
"@types/react": "^19.2.8",
|
|
21
|
+
"@types/react-dom": "^19.2.3",
|
|
22
|
+
"eslint": "^9.39.2",
|
|
23
23
|
"eslint-config-next": "16.1.1",
|
|
24
|
-
"tailwindcss": "^4",
|
|
25
|
-
"typescript": "^5"
|
|
24
|
+
"tailwindcss": "^4.1.18",
|
|
25
|
+
"typescript": "^5.9.3"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
VITE_API_URL=http://localhost:3000
|
|
1
|
+
VITE_API_URL=http://localhost:3000
|
|
@@ -14,6 +14,7 @@ A production-ready React starter template with TypeScript, Vite, and essential l
|
|
|
14
14
|
- **SEO Ready** with React Helmet Async
|
|
15
15
|
- **Error Boundaries** for graceful error handling
|
|
16
16
|
- **ESLint** for code quality
|
|
17
|
+
- **Prettier** for code formatting
|
|
17
18
|
- **Custom Hooks** included
|
|
18
19
|
|
|
19
20
|
## Quick Start
|
|
@@ -30,6 +31,18 @@ pnpm build
|
|
|
30
31
|
|
|
31
32
|
# Preview production build
|
|
32
33
|
pnpm preview
|
|
34
|
+
|
|
35
|
+
# Run linter
|
|
36
|
+
pnpm lint
|
|
37
|
+
|
|
38
|
+
# Fix linting issues
|
|
39
|
+
pnpm lint:fix
|
|
40
|
+
|
|
41
|
+
# Format code
|
|
42
|
+
pnpm format
|
|
43
|
+
|
|
44
|
+
# Check formatting
|
|
45
|
+
pnpm format:check
|
|
33
46
|
```
|
|
34
47
|
|
|
35
48
|
## Project Structure
|
|
@@ -44,6 +57,7 @@ src/
|
|
|
44
57
|
├── pages/ # Route pages
|
|
45
58
|
├── types/ # TypeScript types
|
|
46
59
|
├── utils/ # Helper functions
|
|
60
|
+
├── test/ # Test setup
|
|
47
61
|
├── App.tsx # Main app component
|
|
48
62
|
├── main.tsx # Entry point
|
|
49
63
|
└── index.css # Global styles
|
|
@@ -51,6 +65,14 @@ src/
|
|
|
51
65
|
|
|
52
66
|
## Environment Variables
|
|
53
67
|
|
|
68
|
+
Copy `.env.example` to `.env` and update the values:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cp .env.example .env
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Environment Variables
|
|
75
|
+
|
|
54
76
|
Create a `.env` file:
|
|
55
77
|
|
|
56
78
|
```env
|
|
@@ -12,33 +12,33 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@tailwindcss/vite": "^4.1.18",
|
|
15
|
-
"@tanstack/react-query": "^5.
|
|
16
|
-
"@tanstack/react-query-devtools": "^5.
|
|
17
|
-
"axios": "^1.
|
|
15
|
+
"@tanstack/react-query": "^5.90.16",
|
|
16
|
+
"@tanstack/react-query-devtools": "^5.91.2",
|
|
17
|
+
"axios": "^1.13.2",
|
|
18
18
|
"class-variance-authority": "^0.7.1",
|
|
19
19
|
"clsx": "^2.1.1",
|
|
20
|
-
"react": "^19.2.
|
|
21
|
-
"react-dom": "^19.2.
|
|
20
|
+
"react": "^19.2.3",
|
|
21
|
+
"react-dom": "^19.2.3",
|
|
22
22
|
"react-helmet-async": "^2.0.5",
|
|
23
|
-
"react-hot-toast": "^2.
|
|
23
|
+
"react-hot-toast": "^2.6.0",
|
|
24
24
|
"react-router": "^7.12.0",
|
|
25
25
|
"tailwind-merge": "^3.4.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@eslint/js": "^9.39.
|
|
29
|
-
"@types/node": "^
|
|
30
|
-
"@types/react": "^19.2.
|
|
28
|
+
"@eslint/js": "^9.39.2",
|
|
29
|
+
"@types/node": "^25.0.8",
|
|
30
|
+
"@types/react": "^19.2.8",
|
|
31
31
|
"@types/react-dom": "^19.2.3",
|
|
32
|
-
"@vitejs/plugin-react": "^5.1.
|
|
32
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
33
33
|
"autoprefixer": "^10.4.23",
|
|
34
|
-
"eslint": "^9.39.
|
|
34
|
+
"eslint": "^9.39.2",
|
|
35
35
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
36
|
-
"eslint-plugin-react-refresh": "^0.4.
|
|
37
|
-
"globals": "^
|
|
36
|
+
"eslint-plugin-react-refresh": "^0.4.26",
|
|
37
|
+
"globals": "^17.0.0",
|
|
38
38
|
"postcss": "^8.5.6",
|
|
39
39
|
"tailwindcss": "^4.1.18",
|
|
40
40
|
"typescript": "~5.9.3",
|
|
41
|
-
"typescript-eslint": "^8.
|
|
42
|
-
"vite": "^7.
|
|
41
|
+
"typescript-eslint": "^8.53.0",
|
|
42
|
+
"vite": "^7.3.1"
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { createBrowserRouter } from "react-router";
|
|
2
|
-
import { userService } from "./api/services/user.service";
|
|
3
2
|
import { ErrorBoundary } from "./components/ErrorBoundary";
|
|
4
3
|
import Layout from "./components/Layout";
|
|
5
4
|
import About from "./pages/About";
|
|
6
5
|
import Home from "./pages/Home";
|
|
7
6
|
import NotFound from "./pages/NotFound";
|
|
8
|
-
import UserProfile from "./pages/UserProfile";
|
|
9
7
|
|
|
10
8
|
export const router = createBrowserRouter([
|
|
11
9
|
{
|
|
@@ -15,16 +13,6 @@ export const router = createBrowserRouter([
|
|
|
15
13
|
children: [
|
|
16
14
|
{ index: true, Component: Home },
|
|
17
15
|
{ path: "about", Component: About },
|
|
18
|
-
{
|
|
19
|
-
path: "users/:userId",
|
|
20
|
-
loader: async ({ params }) => {
|
|
21
|
-
const id = params.userId;
|
|
22
|
-
if (!id) throw new Response("Missing user id", { status: 400 });
|
|
23
|
-
const user = await userService.getUser(id);
|
|
24
|
-
return user;
|
|
25
|
-
},
|
|
26
|
-
Component: UserProfile,
|
|
27
|
-
},
|
|
28
16
|
{ path: "*", Component: NotFound },
|
|
29
17
|
],
|
|
30
18
|
},
|