nodexajs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "nodexajs",
3
+ "version": "1.0.0",
4
+ "description": "A scalable Express.js project generator with a standardized architecture",
5
+ "type": "module",
6
+ "main": "src/cli.js",
7
+ "bin": {
8
+ "nodexa": "./src/cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node src/cli.js",
12
+ "dev": "nodemon src/cli.js"
13
+ },
14
+ "keywords": [
15
+ "nodexa",
16
+ "nodejs",
17
+ "express",
18
+ "expressjs",
19
+ "cli",
20
+ "generator",
21
+ "scaffolding"
22
+ ],
23
+ "author": "Dhiraj Mahadik",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "commander": "^15.0.0",
27
+ "inquirer": "^14.2.2"
28
+ },
29
+ "devDependencies": {
30
+ "nodemon": "^3.1.14"
31
+ }
32
+ }
package/src/cli.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { initCommand } from "./commands/init.command.js";
5
+
6
+ const program = new Command();
7
+
8
+ program
9
+ .name("nodexa")
10
+ .description("Nodexa - Node.js and Express.js project generator")
11
+ .version("0.1.0");
12
+
13
+ program
14
+ .command("init")
15
+ .description("Create a new Express.js project")
16
+ .argument("<project-name>", "Name of the project")
17
+ .action(initCommand);
18
+
19
+ program.parse();
@@ -0,0 +1,20 @@
1
+ import { generateProject } from "../generators/project.generator.js";
2
+
3
+ export async function initCommand(projectName) {
4
+ try {
5
+ console.log(`\nCreating Nodexa project: ${projectName}\n`);
6
+
7
+ await generateProject(projectName);
8
+
9
+ console.log("\n✔ Project created successfully!");
10
+ console.log(`\nNext steps:\n`);
11
+ console.log(` cd ${projectName}`);
12
+ console.log(` npm install`);
13
+ console.log(` npm run dev\n`);
14
+ } catch (error) {
15
+ console.error("\n✖ Failed to create project.");
16
+ console.error(error.message);
17
+
18
+ process.exit(1);
19
+ }
20
+ }
@@ -0,0 +1,71 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { copyDirectory } from "../utils/file.utils.js";
5
+ import { spawn } from "child_process";
6
+
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+
12
+ function installDependencies(projectPath) {
13
+ return new Promise((resolve, reject) => {
14
+ const npm = spawn("npm", ["install"], {
15
+ cwd: projectPath,
16
+ stdio: "inherit",
17
+ shell: true,
18
+ });
19
+
20
+ npm.on("close", (code) => {
21
+ if (code === 0) {
22
+ resolve();
23
+ } else {
24
+ reject(
25
+ new Error(`npm install failed with exit code ${code}`),
26
+ );
27
+ }
28
+ });
29
+
30
+ npm.on("error", reject);
31
+ });
32
+ }
33
+
34
+
35
+ export async function generateProject(projectName) {
36
+ const projectPath = path.resolve(process.cwd(), projectName);
37
+
38
+ const templatePath = path.resolve(
39
+ __dirname,
40
+ "../templates/javascript/base",
41
+ );
42
+
43
+ await fs.mkdir(projectPath, {
44
+ recursive: true,
45
+ });
46
+
47
+ await copyDirectory(templatePath, projectPath, {
48
+ PROJECT_NAME: projectName,
49
+ });
50
+
51
+ console.log("\nInstalling dependencies...\n");
52
+
53
+ await installDependencies(projectPath);
54
+
55
+ await fs.writeFile(
56
+ path.join(projectPath, ".env"),
57
+ "PORT=5000\nNODE_ENV=development\nLOG_LEVEL=info\n",
58
+ );
59
+
60
+ await fs.writeFile(
61
+ path.join(projectPath, ".env.example"),
62
+ "PORT=5000\nNODE_ENV=development\nLOG_LEVEL=info\n",
63
+ );
64
+
65
+ await fs.writeFile(
66
+ path.join(projectPath, ".gitignore"),
67
+ "node_modules/\n.env\n.DS_Store\n*.log\ncoverage/\nlogs/\n",
68
+ );
69
+
70
+ console.log(`Created project: ${projectPath}`);
71
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "1.0.0",
4
+ "description": "{{PROJECT_NAME}} API",
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "node --env-file .env src/server.js",
8
+ "dev": "node --watch src/server.js"
9
+ },
10
+ "dependencies": {
11
+ "express": "^5.1.0",
12
+ "winston":"3.19.0"
13
+ }
14
+ }
@@ -0,0 +1,18 @@
1
+ import express from "express";
2
+ import { errorHandler } from "./middlewares/error.middleware.js";
3
+
4
+ const app = express();
5
+
6
+ app.use(express.json());
7
+ app.use(express.urlencoded({ extended: true }));
8
+
9
+ app.get("/", (req, res) => {
10
+ return res.json({
11
+ success: true,
12
+ message: "Nodexa API is running",
13
+ });
14
+ });
15
+
16
+ app.use(errorHandler);
17
+
18
+ export { app };
@@ -0,0 +1,16 @@
1
+ import { asyncHandler } from "../../utils/asyncHandler.js";
2
+ import { ApiResponse } from "../utils/ApiResponse.js";
3
+
4
+ const healthCheck = asyncHandler((req, res) => {
5
+ return res.status(200).json(
6
+ new ApiResponse(
7
+ 200,
8
+ {
9
+ status: "UP",
10
+ },
11
+ "Nodexa API is running",
12
+ ),
13
+ );
14
+ })
15
+
16
+ export { healthCheck };
@@ -0,0 +1 @@
1
+ export { logger } from "./logger.js";
@@ -0,0 +1,47 @@
1
+ import winston from "winston";
2
+
3
+ const { combine, timestamp, printf, colorize, errors } = winston.format;
4
+
5
+ const logFormat = printf(({ level, message, timestamp, stack }) => {
6
+ return `${timestamp} [${level}]: ${stack || message}`;
7
+ });
8
+
9
+ const logger = winston.createLogger({
10
+ level: process.env.LOG_LEVEL || "info",
11
+
12
+ format: combine(
13
+ timestamp({
14
+ format: "YYYY-MM-DD HH:mm:ss",
15
+ }),
16
+ errors({
17
+ stack: true,
18
+ }),
19
+ logFormat,
20
+ ),
21
+
22
+ transports: [
23
+ new winston.transports.Console({
24
+ format: combine(
25
+ colorize(),
26
+ timestamp({
27
+ format: "YYYY-MM-DD HH:mm:ss",
28
+ }),
29
+ errors({
30
+ stack: true,
31
+ }),
32
+ logFormat,
33
+ ),
34
+ }),
35
+
36
+ new winston.transports.File({
37
+ filename: "logs/error.log",
38
+ level: "error",
39
+ }),
40
+
41
+ new winston.transports.File({
42
+ filename: "logs/combined.log",
43
+ }),
44
+ ],
45
+ });
46
+
47
+ export { logger };
@@ -0,0 +1,38 @@
1
+ import { ApiError } from "../utils/ApiError.js";
2
+ import { logger } from "../logger/index.js";
3
+
4
+ const errorHandler = (err, req, res, next) => {
5
+ let error = err;
6
+
7
+ if (!(error instanceof ApiError)) {
8
+ let statusCode = 500;
9
+ let message = "Something went wrong";
10
+
11
+ error = new ApiError(
12
+ statusCode,
13
+ message,
14
+ error?.errors || [],
15
+ err.stack,
16
+ );
17
+ }
18
+
19
+ logger.error(error.message, {
20
+ stack: error.stack,
21
+ method: req.method,
22
+ url: req.originalUrl,
23
+ });
24
+
25
+ const response = {
26
+ ...error,
27
+ message: error.message,
28
+ ...(process.env.NODE_ENV === "development"
29
+ ? {
30
+ stack: error.stack,
31
+ }
32
+ : {}),
33
+ };
34
+
35
+ return res.status(error.statusCode).json(response);
36
+ };
37
+
38
+ export { errorHandler };
@@ -0,0 +1,8 @@
1
+ import { Router } from "express";
2
+ import { healthCheck } from "../controllers/health.controller.js";
3
+
4
+ const router = Router();
5
+
6
+ router.get("/", healthCheck);
7
+
8
+ export { router };
@@ -0,0 +1,9 @@
1
+ import express from "express";
2
+ import healthRoutes from "./health/health.routes.js";
3
+
4
+
5
+ const router = express.Router();
6
+
7
+ router.use("/", healthRoutes);
8
+
9
+ export { router };
@@ -0,0 +1,8 @@
1
+ import { app } from "./app.js";
2
+ import { logger } from "./logger/index.js";
3
+
4
+ const PORT = process.env.PORT || 5000;
5
+
6
+ app.listen(PORT, () => {
7
+ logger.info(`Server running on port ${PORT}`);
8
+ });
@@ -0,0 +1,38 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import { errorHandler } from "../middlewares/error.middleware.js";
3
+
4
+ /**
5
+ * @description Common Error class to throw an error from anywhere.
6
+ * The {@link errorHandler} middleware will catch this error at the central place
7
+ * and it will return an appropriate response to the client.
8
+ */
9
+ class ApiError extends Error {
10
+ /**
11
+ * @param {number} statusCode
12
+ * @param {string} message
13
+ * @param {any[]} errors
14
+ * @param {string} stack
15
+ */
16
+ constructor(
17
+ statusCode,
18
+ message = "Something went wrong",
19
+ errors = [],
20
+ stack = "",
21
+ ) {
22
+ super(message);
23
+
24
+ this.statusCode = statusCode;
25
+ this.data = null;
26
+ this.message = message;
27
+ this.success = false;
28
+ this.errors = errors;
29
+
30
+ if (stack) {
31
+ this.stack = stack;
32
+ } else {
33
+ Error.captureStackTrace(this, this.constructor);
34
+ }
35
+ }
36
+ }
37
+
38
+ export { ApiError };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @description Common response class to send serialized response from anywhere.
3
+ */
4
+ class ApiResponse {
5
+ /**
6
+ * @param {number} statusCode
7
+ * @param {any} data
8
+ * @param {string} message
9
+ */
10
+ constructor(statusCode, data = null, message = "Success") {
11
+ this.statusCode = statusCode;
12
+ this.data = data;
13
+ this.message = message;
14
+ this.success = statusCode < 400;
15
+ }
16
+ }
17
+
18
+ export { ApiResponse };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @param {(req: import("express").Request, res: import("express").Response, next: import("express").NextFunction) => void} requestHandler
3
+ */
4
+ const asyncHandler = (requestHandler) => {
5
+ return (req, res, next) => {
6
+ Promise.resolve(requestHandler(req, res, next)).catch((err) => {
7
+ next(err);
8
+ });
9
+ };
10
+ };
11
+
12
+ export { asyncHandler };
@@ -0,0 +1,40 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+
4
+ export async function copyDirectory(
5
+ source,
6
+ destination,
7
+ variables = {},
8
+ ) {
9
+ await fs.mkdir(destination, {
10
+ recursive: true,
11
+ });
12
+
13
+ const entries = await fs.readdir(source, {
14
+ withFileTypes: true,
15
+ });
16
+
17
+ for (const entry of entries) {
18
+ const sourcePath = path.join(source, entry.name);
19
+ const destinationPath = path.join(destination, entry.name);
20
+
21
+ if (entry.isDirectory()) {
22
+ await copyDirectory(
23
+ sourcePath,
24
+ destinationPath,
25
+ variables,
26
+ );
27
+ } else {
28
+ let content = await fs.readFile(sourcePath, "utf8");
29
+
30
+ for (const [key, value] of Object.entries(variables)) {
31
+ content = content.replaceAll(
32
+ `{{${key}}}`,
33
+ value,
34
+ );
35
+ }
36
+
37
+ await fs.writeFile(destinationPath, content);
38
+ }
39
+ }
40
+ }