create-stackkit-app 0.4.2 → 0.4.3
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 +1 -0
- package/bin/create-stackkit.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/create-project.js +329 -143
- package/dist/lib/template-composer.js +21 -21
- package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +3 -3
- package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +4 -4
- package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +4 -4
- package/modules/auth/better-auth-express/files/lib/auth.ts +1 -1
- package/modules/auth/better-auth-express/files/routes/auth.ts +3 -3
- package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +3 -3
- package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +4 -4
- package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +4 -4
- package/modules/auth/better-auth-nextjs/files/api/auth/[...all]/route.ts +2 -3
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +4 -4
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +2 -2
- package/modules/auth/clerk-express/files/lib/auth.ts +1 -1
- package/modules/auth/clerk-nextjs/files/lib/auth-provider.tsx +1 -1
- package/modules/auth/clerk-nextjs/files/middleware.ts +3 -3
- package/modules/auth/clerk-react/files/lib/auth-provider.tsx +2 -2
- package/modules/database/mongoose-mongodb/files/lib/db.ts +3 -3
- package/modules/database/prisma-mongodb/files/lib/db.ts +2 -2
- package/modules/database/prisma-postgresql/files/lib/db.ts +2 -2
- package/package.json +8 -3
- package/templates/express/package.json +1 -0
- package/templates/express/src/app.ts +17 -15
- package/templates/express/src/config/env.ts +8 -8
- package/templates/express/src/middlewares/error.middleware.ts +3 -3
- package/templates/express/src/server.ts +2 -2
- package/templates/express/template.json +38 -1
- package/templates/express/tsconfig.json +17 -0
- package/templates/nextjs/app/layout.tsx +1 -5
- package/templates/nextjs/app/page.tsx +26 -34
- package/templates/nextjs/package.json +2 -1
- package/templates/nextjs/template.json +13 -1
- package/templates/react-vite/eslint.config.js +9 -9
- package/templates/react-vite/package.json +1 -2
- package/templates/react-vite/src/api/client.ts +16 -16
- package/templates/react-vite/src/api/services/user.service.ts +2 -10
- package/templates/react-vite/src/components/ErrorBoundary.tsx +4 -4
- package/templates/react-vite/src/components/Layout.tsx +1 -1
- package/templates/react-vite/src/components/Loading.tsx +1 -1
- package/templates/react-vite/src/components/SEO.tsx +5 -5
- package/templates/react-vite/src/config/constants.ts +3 -3
- package/templates/react-vite/src/hooks/index.ts +5 -5
- package/templates/react-vite/src/lib/queryClient.ts +2 -2
- package/templates/react-vite/src/main.tsx +12 -12
- package/templates/react-vite/src/pages/About.tsx +6 -2
- package/templates/react-vite/src/pages/Home.tsx +8 -4
- package/templates/react-vite/src/pages/NotFound.tsx +2 -2
- package/templates/react-vite/src/pages/UserProfile.tsx +6 -6
- package/templates/react-vite/src/router.tsx +13 -13
- package/templates/react-vite/src/types/{api.ts → api.d.ts} +6 -6
- package/templates/react-vite/src/types/user.d.ts +6 -0
- package/templates/react-vite/src/utils/helpers.ts +11 -11
- package/templates/react-vite/src/utils/storage.ts +4 -4
- package/templates/react-vite/template.json +26 -0
- package/templates/react-vite/tsconfig.json +1 -4
- package/templates/react-vite/vite.config.ts +5 -5
|
@@ -23,8 +23,8 @@ class TemplateComposer {
|
|
|
23
23
|
const baseFiles = await this.getBaseFiles(framework);
|
|
24
24
|
filesToCopy.push(...baseFiles);
|
|
25
25
|
// 2. Load database configuration if not "none"
|
|
26
|
-
if (database !==
|
|
27
|
-
const dbConfig = await this.loadConfig(path_1.default.join(this.templatesDir,
|
|
26
|
+
if (database !== "none") {
|
|
27
|
+
const dbConfig = await this.loadConfig(path_1.default.join(this.templatesDir, "databases", database));
|
|
28
28
|
// Check compatibility
|
|
29
29
|
if (dbConfig.compatibleWith?.frameworks &&
|
|
30
30
|
!dbConfig.compatibleWith.frameworks.includes(framework)) {
|
|
@@ -32,17 +32,17 @@ class TemplateComposer {
|
|
|
32
32
|
}
|
|
33
33
|
configs.push(dbConfig);
|
|
34
34
|
// Copy database files
|
|
35
|
-
const dbDir = path_1.default.join(this.templatesDir,
|
|
35
|
+
const dbDir = path_1.default.join(this.templatesDir, "databases", database);
|
|
36
36
|
const dbFiles = await this.collectFiles(dbDir);
|
|
37
37
|
filesToCopy.push(...dbFiles.map((f) => ({
|
|
38
38
|
source: f,
|
|
39
|
-
dest: path_1.default.join(targetDir, f.replace(dbDir + path_1.default.sep,
|
|
39
|
+
dest: path_1.default.join(targetDir, f.replace(dbDir + path_1.default.sep, "")),
|
|
40
40
|
})));
|
|
41
41
|
}
|
|
42
42
|
// 3. Load auth configuration if not "none"
|
|
43
|
-
if (auth !==
|
|
43
|
+
if (auth !== "none") {
|
|
44
44
|
const authKey = this.getAuthKey(framework, auth);
|
|
45
|
-
const authDir = path_1.default.join(this.templatesDir,
|
|
45
|
+
const authDir = path_1.default.join(this.templatesDir, "auth", authKey);
|
|
46
46
|
if (await fs_extra_1.default.pathExists(authDir)) {
|
|
47
47
|
const authConfig = await this.loadConfig(authDir);
|
|
48
48
|
// Check compatibility
|
|
@@ -59,7 +59,7 @@ class TemplateComposer {
|
|
|
59
59
|
const authFiles = await this.collectFiles(authDir);
|
|
60
60
|
filesToCopy.push(...authFiles.map((f) => ({
|
|
61
61
|
source: f,
|
|
62
|
-
dest: path_1.default.join(targetDir, f.replace(authDir + path_1.default.sep,
|
|
62
|
+
dest: path_1.default.join(targetDir, f.replace(authDir + path_1.default.sep, "")),
|
|
63
63
|
})));
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -73,8 +73,8 @@ class TemplateComposer {
|
|
|
73
73
|
await this.writeEnvFile(targetDir, mergedConfig);
|
|
74
74
|
}
|
|
75
75
|
async loadConfig(dir) {
|
|
76
|
-
const configPath = path_1.default.join(dir,
|
|
77
|
-
const templatePath = path_1.default.join(dir,
|
|
76
|
+
const configPath = path_1.default.join(dir, "config.json");
|
|
77
|
+
const templatePath = path_1.default.join(dir, "template.json");
|
|
78
78
|
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
79
79
|
return await fs_extra_1.default.readJson(configPath);
|
|
80
80
|
}
|
|
@@ -86,14 +86,14 @@ class TemplateComposer {
|
|
|
86
86
|
async getBaseFiles(framework) {
|
|
87
87
|
// For now, use existing complete template
|
|
88
88
|
// In future, this will use minimal base templates
|
|
89
|
-
const baseDir = path_1.default.join(this.templatesDir,
|
|
89
|
+
const baseDir = path_1.default.join(this.templatesDir, "next-prisma-postgres-shadcn");
|
|
90
90
|
const files = await this.collectFiles(baseDir);
|
|
91
91
|
return files.map((source) => ({
|
|
92
92
|
source,
|
|
93
|
-
dest: source.replace(baseDir,
|
|
93
|
+
dest: source.replace(baseDir, ""),
|
|
94
94
|
}));
|
|
95
95
|
}
|
|
96
|
-
async collectFiles(dir, exclude = [
|
|
96
|
+
async collectFiles(dir, exclude = ["config.json", "template.json", "node_modules"]) {
|
|
97
97
|
const files = [];
|
|
98
98
|
if (!(await fs_extra_1.default.pathExists(dir))) {
|
|
99
99
|
return files;
|
|
@@ -143,7 +143,7 @@ class TemplateComposer {
|
|
|
143
143
|
}, {});
|
|
144
144
|
}
|
|
145
145
|
async writePackageJson(targetDir, config) {
|
|
146
|
-
const pkgPath = path_1.default.join(targetDir,
|
|
146
|
+
const pkgPath = path_1.default.join(targetDir, "package.json");
|
|
147
147
|
let pkg = {};
|
|
148
148
|
if (await fs_extra_1.default.pathExists(pkgPath)) {
|
|
149
149
|
pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
@@ -164,16 +164,16 @@ class TemplateComposer {
|
|
|
164
164
|
if (!config.env || Object.keys(config.env).length === 0) {
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
|
-
const envPath = path_1.default.join(targetDir,
|
|
168
|
-
const envExamplePath = path_1.default.join(targetDir,
|
|
167
|
+
const envPath = path_1.default.join(targetDir, ".env");
|
|
168
|
+
const envExamplePath = path_1.default.join(targetDir, ".env.example");
|
|
169
169
|
const envContent = Object.entries(config.env)
|
|
170
170
|
.map(([key, value]) => `${key}="${value}"`)
|
|
171
|
-
.join(
|
|
171
|
+
.join("\n") + "\n";
|
|
172
172
|
// Append or create .env.example
|
|
173
173
|
if (await fs_extra_1.default.pathExists(envExamplePath)) {
|
|
174
|
-
const existing = await fs_extra_1.default.readFile(envExamplePath,
|
|
174
|
+
const existing = await fs_extra_1.default.readFile(envExamplePath, "utf-8");
|
|
175
175
|
if (!existing.includes(envContent)) {
|
|
176
|
-
await fs_extra_1.default.appendFile(envExamplePath,
|
|
176
|
+
await fs_extra_1.default.appendFile(envExamplePath, "\n" + envContent);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
else {
|
|
@@ -187,9 +187,9 @@ class TemplateComposer {
|
|
|
187
187
|
getAuthKey(framework, auth) {
|
|
188
188
|
// Map framework + auth to specific implementation
|
|
189
189
|
const mapping = {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
190
|
+
"nextjs-nextauth": "nextauth",
|
|
191
|
+
"nextjs-better-auth": "better-auth-nextjs",
|
|
192
|
+
"express-better-auth": "better-auth-express",
|
|
193
193
|
};
|
|
194
194
|
return mapping[`${framework}-${auth}`] || auth;
|
|
195
195
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { betterAuth } from
|
|
2
|
-
import { mongodbAdapter } from
|
|
3
|
-
import { client } from
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
3
|
+
import { client } from "./db";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: mongodbAdapter(client),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { prismaAdapter } from
|
|
2
|
-
import { betterAuth } from
|
|
3
|
-
import { prisma } from
|
|
1
|
+
import { prismaAdapter } from "@better-auth/prisma";
|
|
2
|
+
import { betterAuth } from "better-auth";
|
|
3
|
+
import { prisma } from "./db";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: prismaAdapter(prisma, {
|
|
7
|
-
provider:
|
|
7
|
+
provider: "mongodb",
|
|
8
8
|
}),
|
|
9
9
|
emailAndPassword: {
|
|
10
10
|
enabled: true,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { prismaAdapter } from
|
|
2
|
-
import { betterAuth } from
|
|
3
|
-
import { prisma } from
|
|
1
|
+
import { prismaAdapter } from "@better-auth/prisma";
|
|
2
|
+
import { betterAuth } from "better-auth";
|
|
3
|
+
import { prisma } from "./db";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: prismaAdapter(prisma, {
|
|
7
|
-
provider:
|
|
7
|
+
provider: "postgresql",
|
|
8
8
|
}),
|
|
9
9
|
emailAndPassword: {
|
|
10
10
|
enabled: true,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Router } from
|
|
2
|
-
import { auth } from
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { auth } from "../lib/auth";
|
|
3
3
|
|
|
4
4
|
const router = Router();
|
|
5
5
|
|
|
6
6
|
// Mount Better Auth handlers
|
|
7
|
-
router.all(
|
|
7
|
+
router.all("/auth/*", async (req, res) => {
|
|
8
8
|
const response = await auth.handler(req);
|
|
9
9
|
return res.status(response.status).json(response.body);
|
|
10
10
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { client } from
|
|
2
|
-
import { betterAuth } from
|
|
3
|
-
import { mongodbAdapter } from
|
|
1
|
+
import { client } from "@/lib/db";
|
|
2
|
+
import { betterAuth } from "better-auth";
|
|
3
|
+
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: mongodbAdapter(client),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { prisma } from
|
|
2
|
-
import { prismaAdapter } from
|
|
3
|
-
import { betterAuth } from
|
|
1
|
+
import { prisma } from "@/lib/db";
|
|
2
|
+
import { prismaAdapter } from "@better-auth/prisma";
|
|
3
|
+
import { betterAuth } from "better-auth";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: prismaAdapter(prisma, {
|
|
7
|
-
provider:
|
|
7
|
+
provider: "mongodb",
|
|
8
8
|
}),
|
|
9
9
|
emailAndPassword: {
|
|
10
10
|
enabled: true,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { prisma } from
|
|
2
|
-
import { prismaAdapter } from
|
|
3
|
-
import { betterAuth } from
|
|
1
|
+
import { prisma } from "@/lib/db";
|
|
2
|
+
import { prismaAdapter } from "@better-auth/prisma";
|
|
3
|
+
import { betterAuth } from "better-auth";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: prismaAdapter(prisma, {
|
|
7
|
-
provider:
|
|
7
|
+
provider: "postgresql",
|
|
8
8
|
}),
|
|
9
9
|
emailAndPassword: {
|
|
10
10
|
enabled: true,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { prismaAdapter } from
|
|
2
|
-
import { betterAuth } from
|
|
3
|
-
import { prisma } from
|
|
1
|
+
import { prismaAdapter } from "@better-auth/prisma";
|
|
2
|
+
import { betterAuth } from "better-auth";
|
|
3
|
+
import { prisma } from "./db";
|
|
4
4
|
|
|
5
5
|
export const auth = betterAuth({
|
|
6
6
|
database: prismaAdapter(prisma, {
|
|
7
|
-
provider:
|
|
7
|
+
provider: "postgresql", // Change to 'mongodb' if using MongoDB
|
|
8
8
|
}),
|
|
9
9
|
emailAndPassword: {
|
|
10
10
|
enabled: true,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createAuthClient } from
|
|
1
|
+
import { createAuthClient } from "better-auth/react";
|
|
2
2
|
|
|
3
3
|
export const authClient = createAuthClient({
|
|
4
4
|
/** The base URL of the server (optional if you're using the same domain) */
|
|
5
|
-
baseURL: import.meta.env.VITE_AUTH_URL ||
|
|
5
|
+
baseURL: import.meta.env.VITE_AUTH_URL || "http://localhost:3000",
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
// Export specific methods for convenience
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { authMiddleware } from
|
|
1
|
+
import { authMiddleware } from "@clerk/nextjs";
|
|
2
2
|
|
|
3
3
|
export default authMiddleware({
|
|
4
|
-
publicRoutes: [
|
|
4
|
+
publicRoutes: ["/"],
|
|
5
5
|
});
|
|
6
6
|
|
|
7
7
|
export const config = {
|
|
8
|
-
matcher: [
|
|
8
|
+
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
|
|
9
9
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ClerkProvider } from
|
|
1
|
+
import { ClerkProvider } from "@clerk/clerk-react";
|
|
2
2
|
|
|
3
3
|
const publishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
|
|
4
4
|
|
|
5
5
|
if (!publishableKey) {
|
|
6
|
-
throw new Error(
|
|
6
|
+
throw new Error("Missing Publishable Key");
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import mongoose from
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
2
|
|
|
3
|
-
const MONGODB_URI = process.env.MONGODB_URI ||
|
|
3
|
+
const MONGODB_URI = process.env.MONGODB_URI || "mongodb://localhost:27017/myapp";
|
|
4
4
|
|
|
5
5
|
if (!MONGODB_URI) {
|
|
6
|
-
throw new Error(
|
|
6
|
+
throw new Error("Please define the MONGODB_URI environment variable");
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
let cached = global.mongoose;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PrismaClient } from
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
2
|
|
|
3
3
|
const globalForPrisma = globalThis as unknown as {
|
|
4
4
|
prisma: PrismaClient | undefined;
|
|
@@ -6,4 +6,4 @@ const globalForPrisma = globalThis as unknown as {
|
|
|
6
6
|
|
|
7
7
|
export const db = globalForPrisma.prisma ?? new PrismaClient();
|
|
8
8
|
|
|
9
|
-
if (process.env.NODE_ENV !==
|
|
9
|
+
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PrismaClient } from
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
2
|
|
|
3
3
|
const globalForPrisma = globalThis as unknown as {
|
|
4
4
|
prisma: PrismaClient | undefined;
|
|
@@ -6,4 +6,4 @@ const globalForPrisma = globalThis as unknown as {
|
|
|
6
6
|
|
|
7
7
|
export const db = globalForPrisma.prisma ?? new PrismaClient();
|
|
8
8
|
|
|
9
|
-
if (process.env.NODE_ENV !==
|
|
9
|
+
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-stackkit-app",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Create a new StackKit project with modular composition",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"@types/inquirer": "^9.0.7",
|
|
52
52
|
"@types/node": "^20.11.0",
|
|
53
53
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
54
|
-
"typescript": "^5.3.3"
|
|
54
|
+
"typescript": "^5.3.3",
|
|
55
|
+
"recast": "^0.20.5",
|
|
56
|
+
"@babel/core": "^7.28.5",
|
|
57
|
+
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
58
|
+
"@babel/parser": "^7.28.5",
|
|
59
|
+
"@babel/plugin-transform-react-jsx": "^7.27.1"
|
|
55
60
|
}
|
|
56
|
-
}
|
|
61
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import compression from
|
|
2
|
-
import cors from
|
|
3
|
-
import express, { Application, NextFunction, Request, Response } from
|
|
4
|
-
import rateLimit from
|
|
5
|
-
import helmet from
|
|
6
|
-
import morgan from
|
|
7
|
-
import { env } from
|
|
8
|
-
import { errorHandler } from
|
|
1
|
+
import compression from "compression";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import express, { Application, NextFunction, Request, Response } from "express";
|
|
4
|
+
import rateLimit from "express-rate-limit";
|
|
5
|
+
import helmet from "helmet";
|
|
6
|
+
import morgan from "morgan";
|
|
7
|
+
import { env } from "./config/env";
|
|
8
|
+
import { errorHandler } from "./middlewares/error.middleware";
|
|
9
9
|
|
|
10
10
|
// app initialization
|
|
11
11
|
const app: Application = express();
|
|
@@ -13,7 +13,7 @@ app.use(express.json());
|
|
|
13
13
|
|
|
14
14
|
// trust proxy when behind reverse proxy (like Heroku, Vercel, Load Balancers)
|
|
15
15
|
if (env.app.trust_proxy) {
|
|
16
|
-
app.set(
|
|
16
|
+
app.set("trust proxy", 1);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// security headers
|
|
@@ -33,9 +33,9 @@ app.use(limiter);
|
|
|
33
33
|
|
|
34
34
|
// logging
|
|
35
35
|
if (env.node.isProduction) {
|
|
36
|
-
app.use(morgan(
|
|
36
|
+
app.use(morgan("combined"));
|
|
37
37
|
} else {
|
|
38
|
-
app.use(morgan(
|
|
38
|
+
app.use(morgan("dev"));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// cors configuration
|
|
@@ -43,15 +43,17 @@ app.use(
|
|
|
43
43
|
cors({
|
|
44
44
|
origin: [env.app.site_url],
|
|
45
45
|
credentials: true,
|
|
46
|
-
})
|
|
46
|
+
}),
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
// Home page route
|
|
50
|
-
app.get(
|
|
50
|
+
app.get("/", (req: Request, res: Response) => {
|
|
51
51
|
res.json({
|
|
52
|
-
title:
|
|
52
|
+
title: "Welcome to your Express app",
|
|
53
53
|
description:
|
|
54
|
-
|
|
54
|
+
"Built with StackKit - A production-ready Express template with TypeScript, security, and best practices.",
|
|
55
|
+
version: "1.0.0",
|
|
56
|
+
docs: "https://github.com/tariqul420/stackkit",
|
|
55
57
|
});
|
|
56
58
|
});
|
|
57
59
|
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import dotenv from
|
|
2
|
-
import path from
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import path from "path";
|
|
3
3
|
|
|
4
|
-
dotenv.config({ path: path.join(process.cwd(),
|
|
4
|
+
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
5
5
|
|
|
6
6
|
const env = {
|
|
7
7
|
app: {
|
|
8
8
|
port: Number(process.env.PORT) || 3000,
|
|
9
|
-
url: process.env.APP_URL ||
|
|
10
|
-
site_url: process.env.SITE_URL ||
|
|
11
|
-
trust_proxy: (process.env.TRUST_PROXY ||
|
|
9
|
+
url: process.env.APP_URL || "http://localhost:3000",
|
|
10
|
+
site_url: process.env.SITE_URL || "http://localhost:5173",
|
|
11
|
+
trust_proxy: (process.env.TRUST_PROXY || "false") === "true",
|
|
12
12
|
rateLimit: {
|
|
13
13
|
max: Number(process.env.RATE_LIMIT_MAX) || 100,
|
|
14
14
|
windowMs: Number(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
17
|
node: {
|
|
18
|
-
env: process.env.NODE_ENV ||
|
|
19
|
-
isProduction: (process.env.NODE_ENV ||
|
|
18
|
+
env: process.env.NODE_ENV || "development",
|
|
19
|
+
isProduction: (process.env.NODE_ENV || "development") === "production",
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { NextFunction, Request, Response } from
|
|
2
|
-
import { env } from
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { env } from "../config/env";
|
|
3
3
|
|
|
4
4
|
export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
|
|
5
5
|
const statusCode = err.status || 500;
|
|
6
|
-
const errorMessage = err?.message ||
|
|
6
|
+
const errorMessage = err?.message || "Internal server error!";
|
|
7
7
|
|
|
8
8
|
const payload: any = {
|
|
9
9
|
success: false,
|
|
@@ -3,5 +3,42 @@
|
|
|
3
3
|
"displayName": "Express.js",
|
|
4
4
|
"framework": "express",
|
|
5
5
|
"description": "Express.js REST API with TypeScript",
|
|
6
|
-
"files": ["src/", ".env.example", ".gitignore", "package.json", "tsconfig.json"]
|
|
6
|
+
"files": ["src/", ".env.example", ".gitignore", "package.json", "tsconfig.json"],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "tsx watch src/server.ts",
|
|
9
|
+
"clean": "rimraf dist",
|
|
10
|
+
"prebuild": "npm run clean",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"lint": "eslint src --ext .ts",
|
|
13
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
14
|
+
"start": "node dist/server.js",
|
|
15
|
+
"start:prod": "cross-env NODE_ENV=production node dist/server.js"
|
|
16
|
+
},
|
|
17
|
+
"jsScripts": {
|
|
18
|
+
"dev": "tsx --watch src/server.js",
|
|
19
|
+
"clean": "rimraf dist",
|
|
20
|
+
"prebuild": "npm run clean",
|
|
21
|
+
"build": "echo 'No build step for JavaScript'",
|
|
22
|
+
"lint": "eslint src --ext .js",
|
|
23
|
+
"lint:fix": "eslint src --ext .js --fix",
|
|
24
|
+
"start": "node src/server.js",
|
|
25
|
+
"start:prod": "cross-env NODE_ENV=production node src/server.js"
|
|
26
|
+
},
|
|
27
|
+
"fileReplacements": [
|
|
28
|
+
{
|
|
29
|
+
"file": "src/server.js",
|
|
30
|
+
"from": "import app from './app'",
|
|
31
|
+
"to": "import app from './app.js'"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"file": "src/app.js",
|
|
35
|
+
"from": "import { env } from './config/env'",
|
|
36
|
+
"to": "import { env } from './config/env.js'"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"file": "src/app.js",
|
|
40
|
+
"from": "import { errorHandler } from './middlewares/error.middleware'",
|
|
41
|
+
"to": "import { errorHandler } from './middlewares/error.middleware.js'"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
7
44
|
}
|
|
@@ -5,8 +5,25 @@
|
|
|
5
5
|
"module": "ESNext",
|
|
6
6
|
"moduleResolution": "node",
|
|
7
7
|
"target": "ES2023",
|
|
8
|
+
"lib": ["ES2023"],
|
|
8
9
|
"strict": true,
|
|
9
10
|
"esModuleInterop": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"declaration": false,
|
|
17
|
+
"declarationMap": false,
|
|
18
|
+
"removeComments": false,
|
|
19
|
+
"noImplicitAny": true,
|
|
20
|
+
"noImplicitReturns": true,
|
|
21
|
+
"noImplicitThis": true,
|
|
22
|
+
"noUnusedLocals": true,
|
|
23
|
+
"noUnusedParameters": true,
|
|
24
|
+
"exactOptionalPropertyTypes": true,
|
|
25
|
+
"noEmitOnError": true,
|
|
26
|
+
"incremental": false,
|
|
10
27
|
"ignoreDeprecations": "5.0"
|
|
11
28
|
},
|
|
12
29
|
"include": ["src/**/*"],
|
|
@@ -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
|
}
|