Skip to content

Commit 168b8da

Browse files
committed
feat: create-module (cm) file and script
1 parent 018a106 commit 168b8da

File tree

2 files changed

+118
-3
lines changed

2 files changed

+118
-3
lines changed

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22
"name": "backend-template-typescript-mongoose-express",
33
"version": "1.0.0",
44
"description": "",
5+
"author": {
6+
"name": "Rahad-Ullah",
7+
"email": "rahadullah10@gmail.com",
8+
"github": "https://github.com/Rahad-Ullah"
9+
},
10+
"license": "ISC",
11+
"keywords": ["node", "express", "typescript", "mongodb", "mongoose", "rest api", "template"],
512
"main": "index.js",
613
"scripts": {
714
"dev": "ts-node-dev --respawn --transpile-only src/dev-server.ts",
815
"start": "node dist/server.js",
916
"build": "tsc",
17+
"cm": "ts-node ./src/cm.ts",
1018
"lint:check": "eslint --ignore-path .eslintignore --ext .js,.ts",
1119
"lint:fix": "lint . --fix",
1220
"prettier:check": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\"",
1321
"prettier:fix": "prettier --write .",
1422
"test": "echo \"Error: no test specified\" && exit 1"
1523
},
16-
"keywords": [],
17-
"author": "Rahad-Ullah",
18-
"license": "ISC",
1924
"devDependencies": {
2025
"@types/bcrypt": "^5.0.2",
2126
"@types/bcryptjs": "^2.4.6",

src/cm.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
function toCamelCase(str: string): string {
5+
return str
6+
.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, (match, index) =>
7+
index === 0 ? match.toUpperCase() : match.toLowerCase()
8+
)
9+
.replace(/\s+/g, '');
10+
}
11+
12+
type Templates = {
13+
interface: string;
14+
model: string;
15+
controller: string;
16+
service: string;
17+
route: string;
18+
validation: string;
19+
constants: string;
20+
};
21+
22+
function createModule(name: string): void {
23+
const camelCaseName = toCamelCase(name);
24+
const folderName = camelCaseName.toLowerCase();
25+
const folderPath = path.join(__dirname, 'app', 'modules', folderName);
26+
27+
// Check if the folder already exists
28+
if (!fs.existsSync(folderPath)) {
29+
fs.mkdirSync(folderPath);
30+
console.log(`Created folder: ${folderName}`);
31+
} else {
32+
console.log(`Folder ${folderName} already exists.`);
33+
return;
34+
}
35+
36+
const templates: Templates = {
37+
interface: `import { Model } from 'mongoose';\n\nexport type I${camelCaseName} = {\n // Define the interface for ${camelCaseName} here\n};\n\nexport type ${camelCaseName}Model = Model<I${camelCaseName}>;\n`,
38+
model: `import { Schema, model } from 'mongoose';\nimport { I${camelCaseName}, ${camelCaseName}Model } from './${folderName}.interface'; \n\nconst ${folderName}Schema = new Schema<I${camelCaseName}, ${camelCaseName}Model>({\n // Define schema fields here\n});\n\nexport const ${camelCaseName} = model<I${camelCaseName}, ${camelCaseName}Model>('${camelCaseName}', ${folderName}Schema);\n`,
39+
controller: `import { Request, Response, NextFunction } from 'express';\nimport { ${camelCaseName}Services } from './${folderName}.service';\n\nexport const ${camelCaseName}Controller = { };\n`,
40+
service: `import { ${camelCaseName}Model } from './${folderName}.interface';\n\nexport const ${camelCaseName}Services = { };\n`,
41+
route: `import express from 'express';\nimport { ${camelCaseName}Controller } from './${folderName}.controller';\n\nconst router = express.Router();\n\nrouter.get('/', ${camelCaseName}Controller); \n\nexport const ${camelCaseName}Routes = router;\n`,
42+
validation: `import { z } from 'zod';\n\nexport const ${camelCaseName}Validations = { };\n`,
43+
constants: `export const ${camelCaseName.toUpperCase()}_CONSTANT = 'someValue';\n`,
44+
};
45+
46+
Object.entries(templates).forEach(([key, content]) => {
47+
const filePath = path.join(folderPath, `${folderName}.${key}.ts`);
48+
fs.writeFileSync(filePath, content);
49+
console.log(`Created file: ${filePath}`);
50+
});
51+
52+
// Add the new module to the central `apiRoutes` array
53+
updateRouterFile(folderName, camelCaseName);
54+
}
55+
56+
// Get the module name from command line arguments
57+
const moduleName: string | undefined = process.argv[2];
58+
if (!moduleName) {
59+
console.log(
60+
'Please provide a module name, e.g., node generateModule UserProfile'
61+
);
62+
} else {
63+
createModule(moduleName);
64+
}
65+
66+
/**
67+
* Updates the central router file by adding a new module route import and entry.
68+
*
69+
* @param folderName - The name of the folder/module (in lowercase or kebab-case).
70+
* @param camelCaseName - The camelCase name of the module (used for route import/export).
71+
*/
72+
function updateRouterFile(folderName: string, camelCaseName: string): void {
73+
const routerPath = path.join(__dirname, 'app/routes', 'index.ts');
74+
const routeImport = `import { ${camelCaseName}Routes } from '../app/modules/${folderName}/${folderName}.route';`;
75+
const routeEntry = `{ path: '/${folderName}', route: ${camelCaseName}Routes }`;
76+
77+
let routerFileContent = fs.readFileSync(routerPath, 'utf-8');
78+
79+
// Check if the import statement is already present
80+
if (!routerFileContent.includes(routeImport)) {
81+
routerFileContent = `${routeImport}\n${routerFileContent}`;
82+
}
83+
84+
// Find the `apiRoutes` array and update it
85+
const apiRoutesRegex =
86+
/export const apiRoutes: \{ path: string; route: any \}\[] = \[([\s\S]*?)\]/;
87+
const match = routerFileContent.match(apiRoutesRegex);
88+
89+
if (match) {
90+
const currentRoutes = match[1].trim();
91+
if (!currentRoutes.includes(routeEntry)) {
92+
const updatedRoutes = currentRoutes
93+
? `${currentRoutes}\n ${routeEntry}`
94+
: `${routeEntry}`;
95+
routerFileContent = routerFileContent.replace(
96+
apiRoutesRegex,
97+
`export const apiRoutes: { path: string; route: any }[] = [\n ${updatedRoutes}\n]`
98+
);
99+
}
100+
} else {
101+
console.error(
102+
'Failed to find apiRoutes array. Ensure the app.ts file has a properly defined apiRoutes array.'
103+
);
104+
return;
105+
}
106+
107+
// Write the updated content back to the `app.ts` file
108+
fs.writeFileSync(routerPath, routerFileContent, 'utf-8');
109+
console.log(`✅ Added route for ${camelCaseName} to central router.`);
110+
}

0 commit comments

Comments
 (0)