@ifecodes/backend-template 1.0.10 → 1.1.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.
package/README.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # 🚀 Backend Template Generator
2
2
 
3
- A powerful CLI tool to generate production-ready Node.js + TypeScript backend applications with Express.js. Supports both monolith and microservice architectures with optional features like authentication, CORS, rate limiting, and more.
3
+ A powerful CLI tool to generate production-ready Node.js backend applications with Express.js. Supports both **TypeScript** and **JavaScript**, with monolith and microservice architectures, and optional features like authentication, CORS, rate limiting, and more.
4
+
5
+ ---
6
+
7
+ ## ✨ Features
8
+
9
+ - 🎯 **TypeScript & JavaScript Support** - Choose your preferred language
10
+ - 🏗️ **Dual Architecture** - Monolith or Microservice
11
+ - 🐳 **Docker Ready** - Containerized microservices
12
+ - ⚡ **PM2 Support** - Process management for production
13
+ - 🔐 **JWT Authentication** - Built-in auth with MongoDB
14
+ - 🛡️ **Security First** - CORS, Helmet, Rate Limiting
15
+ - 📝 **Professional Logging** - Morgan + Winston
16
+ - 🎨 **Colored CLI** - Beautiful Vite-like terminal output
17
+ - 📋 **Project Metadata** - Description, author, and keywords support
4
18
 
5
19
  ---
6
20
 
@@ -35,21 +49,36 @@ npx @ifecodes/backend-template my-project micro
35
49
 
36
50
  When you run the CLI, you'll be prompted to choose:
37
51
 
38
- ### 1. **Project Type**
52
+ ### 1. **Language**
53
+
54
+ - **TypeScript** (default) - Full type safety and modern tooling
55
+ - **JavaScript** - Transpiled from TypeScript for simplicity
56
+
57
+ ### 2. **Project Metadata**
58
+
59
+ - **Description** - Project description for package.json
60
+ - **Author** - Your name or organization
61
+ - **Keywords** - Comma-separated keywords for discoverability
62
+
63
+ ### 3. **Project Type**
64
+
39
65
  - **Monolith API** - Traditional single-server architecture
40
66
  - **Microservice** - Distributed services with API Gateway
41
67
 
42
- ### 2. **Deployment Mode** (Microservices only)
68
+ ### 4. **Deployment Mode** (Microservices only)
69
+
43
70
  - **Docker** - Container-based deployment with docker-compose
44
71
  - **PM2** - Process manager for Node.js applications
45
72
 
46
- ### 3. **Optional Features**
73
+ ### 5. **Optional Features**
74
+
47
75
  - ✅ **CORS** - Cross-Origin Resource Sharing
48
76
  - ✅ **Helmet** - Security headers middleware
49
77
  - ✅ **Rate Limiting** - API request throttling
50
78
  - ✅ **Morgan** - HTTP request logger
51
79
 
52
- ### 4. **Authentication**
80
+ ### 6. **Authentication**
81
+
53
82
  - ✅ **JWT Authentication** with MongoDB
54
83
  - Choose between **bcrypt** (recommended for Windows) or **argon2** for password hashing
55
84
 
@@ -154,7 +183,7 @@ pm2 stop all
154
183
  ## 🛠 Tech Stack
155
184
 
156
185
  - **Runtime**: Node.js (v18+)
157
- - **Language**: TypeScript
186
+ - **Language**: TypeScript or JavaScript
158
187
  - **Framework**: Express.js
159
188
  - **Database**: MongoDB (with Mongoose, if auth enabled)
160
189
  - **Authentication**: JWT + bcrypt/argon2
@@ -165,20 +194,46 @@ pm2 stop all
165
194
 
166
195
  ---
167
196
 
197
+ ## � TypeScript vs JavaScript
198
+
199
+ This CLI generates **TypeScript** projects by default but fully supports **JavaScript** through intelligent transformation:
200
+
201
+ ### TypeScript (Default)
202
+
203
+ - Full type safety and IntelliSense
204
+ - Modern ECMAScript features
205
+ - Compile-time error checking
206
+ - Better tooling and refactoring support
207
+
208
+ ### JavaScript
209
+
210
+ - Automatically transpiled from TypeScript templates
211
+ - Type annotations removed for cleaner code
212
+ - Dependencies adjusted (no @types packages)
213
+ - Same functionality, simpler syntax
214
+
215
+ **Note**: When selecting JavaScript, the CLI transforms the TypeScript template on-the-fly, ensuring you get a production-ready JavaScript project with all the same features.
216
+
217
+ ---
218
+
168
219
  ## 🌟 Features
169
220
 
170
221
  ### ✅ Smart Defaults
222
+
171
223
  - Auto-generates README with project-specific instructions
172
224
  - Creates `.env` from `.env.example` with default values
173
225
  - Configures TypeScript paths for clean imports (`@/config`, `@/utils`)
226
+ - Project metadata (description, author, keywords) in package.json
174
227
 
175
228
  ### ✅ Microservice Architecture
229
+
176
230
  - **API Gateway** on port 4000 (single entry point)
177
231
  - **Service Discovery** - Automatically routes to correct service
178
232
  - **Shared Folder** - Common utilities across all services
179
233
  - **Health Checks** - Built-in monitoring endpoints
180
234
 
181
235
  ### ✅ Developer Experience
236
+
182
237
  - **Hot Reload** - Development server with nodemon
183
238
  - **ESLint** - Code quality enforcement
184
239
  - **Git Hooks** - Pre-commit linting with Husky
@@ -246,6 +301,7 @@ npx @ifecodes/backend-template
246
301
  ```
247
302
 
248
303
  The CLI will:
304
+
249
305
  - Create the new service
250
306
  - Update `docker-compose.yml` or `pm2.config.js`
251
307
  - Configure routing in the API Gateway
package/bin/cli.js CHANGED
@@ -3,9 +3,11 @@ import fs from "fs";
3
3
  import path from "path";
4
4
  import { execSync } from "child_process";
5
5
  import { fileURLToPath } from "url";
6
+ import pc from "picocolors";
6
7
  import { getProjectConfig } from "./lib/prompts.js";
7
8
  import { setupService } from "./lib/service-setup.js";
8
9
  import { generateReadme } from "./lib/readme-generator.js";
10
+ import { stripTypeScript, getJavaScriptScripts, getJavaScriptDependencies } from "./lib/ts-to-js.js";
9
11
  import {
10
12
  generateDockerCompose,
11
13
  generatePm2Config,
@@ -42,20 +44,118 @@ if (isInMicroserviceProject) {
42
44
  // Validate and prepare project
43
45
  if (!isInMicroserviceProject && config.projectType === "microservice") {
44
46
  if (isExistingProject) {
45
- console.error(`\n❌ Error: Project '${sanitizedName}' already exists!`);
47
+ console.error(`\n${pc.red("❌ Error:")} Project ${pc.bold(sanitizedName)} already exists!`);
46
48
  process.exit(1);
47
49
  }
48
50
  console.log(
49
- `\n🏗️ Creating microservices: ${servicesToCreate.join(", ")}...\n`
51
+ `\n${pc.cyan("🏗️ Creating microservices:")} ${pc.bold(servicesToCreate.join(", "))}...\n`
50
52
  );
51
53
  } else if (!isInMicroserviceProject && config.projectType === "monolith") {
52
54
  if (isExistingProject) {
53
- console.error(`\n❌ Error: Project '${sanitizedName}' already exists!`);
55
+ console.error(`\n${pc.red("❌ Error:")} Project ${pc.bold(sanitizedName)} already exists!`);
54
56
  process.exit(1);
55
57
  }
56
58
  fs.cpSync(base, target, { recursive: true });
59
+
60
+ // Transform to JavaScript if selected
61
+ if (config.language === "javascript") {
62
+ // console.log("\n🔄 Converting TypeScript to JavaScript...\n");
63
+ console.log(`\n${pc.cyan("⚙️ Setting up JavaScript project...")}\n`);
64
+ transformToJavaScript(target);
65
+ }
57
66
  } else if (isInMicroserviceProject) {
58
- console.log(`\n🏗️ Adding service: ${servicesToCreate[0]}...\n`);
67
+ console.log(`\n${pc.cyan("🏗️ Adding service:")} ${pc.bold(servicesToCreate[0])}...\n`);
68
+ }
69
+
70
+ // Helper function to transform TypeScript project to JavaScript
71
+ function transformToJavaScript(projectRoot) {
72
+ // Recursively find and transform all .ts files
73
+ function transformDirectory(dir) {
74
+ const items = fs.readdirSync(dir);
75
+
76
+ for (const item of items) {
77
+ const fullPath = path.join(dir, item);
78
+ const stat = fs.statSync(fullPath);
79
+
80
+ if (stat.isDirectory()) {
81
+ // Skip node_modules and dist
82
+ if (item !== 'node_modules' && item !== 'dist') {
83
+ transformDirectory(fullPath);
84
+ }
85
+ } else if (item.endsWith('.ts') && !item.endsWith('.d.ts')) {
86
+ // Transform TypeScript file to JavaScript
87
+ const content = fs.readFileSync(fullPath, 'utf8');
88
+ const jsContent = stripTypeScript(content);
89
+ const jsPath = fullPath.replace(/\.ts$/, '.js');
90
+
91
+ fs.writeFileSync(jsPath, jsContent);
92
+ fs.unlinkSync(fullPath); // Remove original .ts file
93
+ }
94
+ }
95
+ }
96
+
97
+ transformDirectory(path.join(projectRoot, 'src'));
98
+
99
+ // Remove TypeScript config file
100
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
101
+ if (fs.existsSync(tsconfigPath)) {
102
+ fs.unlinkSync(tsconfigPath);
103
+ }
104
+
105
+ // Remove .eslintrc.json and replace with JS version
106
+ const eslintrcPath = path.join(projectRoot, '.eslintrc.json');
107
+ if (fs.existsSync(eslintrcPath)) {
108
+ fs.unlinkSync(eslintrcPath);
109
+ }
110
+
111
+ // Update package.json
112
+ const packageJsonPath = path.join(projectRoot, 'package.json');
113
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
114
+
115
+ // Update scripts
116
+ packageJson.scripts = {
117
+ ...packageJson.scripts,
118
+ ...getJavaScriptScripts()
119
+ };
120
+
121
+ // Update type
122
+ packageJson.type = "module";
123
+
124
+ // Remove main field
125
+ delete packageJson.main;
126
+
127
+ // Update dependencies
128
+ const { dependencies, devDependencies } = getJavaScriptDependencies(
129
+ packageJson.dependencies,
130
+ packageJson.devDependencies
131
+ );
132
+
133
+ packageJson.dependencies = dependencies;
134
+ packageJson.devDependencies = devDependencies;
135
+
136
+ // Remove _moduleAliases (not needed for ES modules with import maps)
137
+ delete packageJson._moduleAliases;
138
+
139
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
140
+
141
+ // Create simple .eslintrc.json for JavaScript
142
+ const eslintConfig = {
143
+ env: {
144
+ node: true,
145
+ es2021: true
146
+ },
147
+ extends: ["eslint:recommended", "prettier"],
148
+ parserOptions: {
149
+ ecmaVersion: "latest",
150
+ sourceType: "module"
151
+ },
152
+ rules: {}
153
+ };
154
+
155
+ fs.writeFileSync(
156
+ path.join(projectRoot, '.eslintrc.json'),
157
+ JSON.stringify(eslintConfig, null, 2) + '\n'
158
+ );
59
159
  }
60
160
 
61
161
  // Process services
@@ -64,7 +164,7 @@ if (isInMicroserviceProject || config.projectType === "microservice") {
64
164
  if (!isInMicroserviceProject) {
65
165
  const sharedDir = path.join(target, "shared");
66
166
  if (!fs.existsSync(sharedDir)) {
67
- console.log(`\n📦 Creating shared folder for config and utils...`);
167
+ console.log(`\n${pc.cyan("📦 Creating shared folder for config and utils...")}`);
68
168
  fs.mkdirSync(sharedDir, { recursive: true });
69
169
 
70
170
  // Copy config and utils from base template
@@ -180,7 +280,7 @@ if (isInMicroserviceProject || config.projectType === "microservice") {
180
280
 
181
281
  // Generate README.md
182
282
  if (!isInMicroserviceProject) {
183
- console.log("\n📝 Generating README.md...\n");
283
+ console.log(`\n${pc.cyan("📝 Generating README.md...")}\n`);
184
284
  const readmeContent = generateReadme(config);
185
285
  fs.writeFileSync(path.join(target, "README.md"), readmeContent);
186
286
 
@@ -207,7 +307,7 @@ if (!isInMicroserviceProject) {
207
307
  }
208
308
 
209
309
  // Generate .env from .env.example for each service or root
210
- console.log("📄 Setting up environment files...\n");
310
+ console.log(`${pc.cyan("📄 Setting up environment files...")}\n`);
211
311
  if (config.projectType === "microservice") {
212
312
  const servicesDir = path.join(target, "services");
213
313
  const allServices = fs.readdirSync(servicesDir).filter((f) =>
@@ -251,14 +351,14 @@ if (!isInMicroserviceProject) {
251
351
  } else if (config.projectType === "monolith") {
252
352
  // Only setup Husky if installation succeeded
253
353
  if (config.installSucceeded) {
254
- console.log("\n🔧 Setting up Husky...\n");
354
+ console.log(`\n${pc.cyan("🔧 Setting up Husky...")}\n`);
255
355
  try {
256
356
  execSync("npm run prepare", { cwd: target, stdio: "inherit" });
257
357
  } catch (error) {
258
- console.log("\n⚠️ Husky setup failed (run 'npm run prepare' manually after fixing dependencies)\n");
358
+ console.log(`\n${pc.yellow("⚠️ Husky setup failed")} ${pc.dim("(run 'npm run prepare' manually after fixing dependencies)")}\n`);
259
359
  }
260
360
  } else {
261
- console.log("\n⚠️ Husky setup skipped (run 'npm install && npm run prepare' to set up git hooks)\n");
361
+ console.log(`\n${pc.yellow("⚠️ Husky setup skipped")} ${pc.dim("(run 'npm install && npm run prepare' to set up git hooks)")}\n`);
262
362
  }
263
363
  }
264
364
  }
@@ -272,27 +372,27 @@ const allServices = fs.existsSync(servicesDir)
272
372
  : servicesToCreate;
273
373
 
274
374
  if (isInMicroserviceProject) {
275
- console.log(`\n✅ Service '${servicesToCreate[0]}' added successfully!`);
276
- console.log(`\n📦 All services: ${allServices.join(", ")}`);
277
- console.log(`\n💡 Next steps:`);
375
+ console.log(`\n${pc.green("✅ Service")} ${pc.bold(servicesToCreate[0])} ${pc.green("added successfully!")}`);
376
+ console.log(`\n${pc.cyan("📦 All services:")} ${allServices.join(", ")}`);
377
+ console.log(`\n${pc.blue("💡 Next steps:")}`);
278
378
  console.log(
279
379
  mode === "docker"
280
- ? ` 1. Start services: docker-compose up`
281
- : ` 1. Start services: pm2 start pm2.config.js`
380
+ ? ` ${pc.dim("1.")} Start services: ${pc.bold("docker-compose up")}`
381
+ : ` ${pc.dim("1.")} Start services: ${pc.bold("pm2 start pm2.config.js")}`
282
382
  );
283
383
  } else if (config.projectType === "microservice") {
284
- console.log("\n✅ Backend created successfully!");
285
- console.log(`\n📦 Created services: ${servicesToCreate.join(", ")}`);
286
- console.log(`\n💡 Next steps:`);
287
- console.log(` 1. cd ${sanitizedName}`);
384
+ console.log(`\n${pc.green("✅ Backend created successfully!")}`);
385
+ console.log(`\n${pc.cyan("📦 Created services:")} ${servicesToCreate.join(", ")}`);
386
+ console.log(`\n${pc.blue("💡 Next steps:")}`);
387
+ console.log(` ${pc.dim("1.")} cd ${pc.bold(sanitizedName)}`);
288
388
  console.log(
289
389
  mode === "docker"
290
- ? ` 2. Start services: docker-compose up`
291
- : ` 2. Start services: pm2 start pm2.config.js`
390
+ ? ` ${pc.dim("2.")} Start services: ${pc.bold("docker-compose up")}`
391
+ : ` ${pc.dim("2.")} Start services: ${pc.bold("pm2 start pm2.config.js")}`
292
392
  );
293
393
  } else {
294
- console.log("\n✅ Backend created successfully!");
295
- console.log(`\n💡 Next steps:`);
296
- console.log(` 1. cd ${sanitizedName}`);
297
- console.log(` 2. npm run dev`);
394
+ console.log(`\n${pc.green("✅ Backend created successfully!")}`);
395
+ console.log(`\n${pc.blue("💡 Next steps:")}`);
396
+ console.log(` ${pc.dim("1.")} cd ${pc.bold(sanitizedName)}`);
397
+ console.log(` ${pc.dim("2.")} npm run dev`);
298
398
  }
@@ -1,4 +1,5 @@
1
1
  import prompts from "prompts";
2
+ import pc from "picocolors";
2
3
  import fs from "fs";
3
4
  import path from "path";
4
5
 
@@ -44,34 +45,44 @@ export const getProjectConfig = async () => {
44
45
  {
45
46
  type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
46
47
  name: "name",
47
- message: "Project name",
48
+ message: pc.cyan("Project name"),
48
49
  initial: "my-backend",
49
50
  },
51
+ {
52
+ type: isInMicroserviceProject || isCI ? null : "select",
53
+ name: "language",
54
+ message: pc.cyan("Select language"),
55
+ choices: [
56
+ { title: pc.green("TypeScript"), value: "typescript" },
57
+ { title: pc.yellow("JavaScript"), value: "javascript" },
58
+ ],
59
+ initial: 0,
60
+ },
50
61
  {
51
62
  type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
52
63
  name: "description",
53
- message: "Project description (optional)",
64
+ message: pc.cyan("Project description") + pc.dim(" (optional)"),
54
65
  initial: "",
55
66
  },
56
67
  {
57
68
  type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
58
69
  name: "author",
59
- message: "Author (optional)",
70
+ message: pc.cyan("Author") + pc.dim(" (optional)"),
60
71
  initial: "",
61
72
  },
62
73
  {
63
74
  type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
64
75
  name: "keywords",
65
- message: "Keywords (comma-separated, optional)",
76
+ message: pc.cyan("Keywords") + pc.dim(" (comma-separated, optional)"),
66
77
  initial: "",
67
78
  },
68
79
  {
69
80
  type: isInMicroserviceProject || (hasCliArgs && cliProjectType) || isCI ? null : "select",
70
81
  name: "projectType",
71
- message: "Project type",
82
+ message: pc.cyan("Project type"),
72
83
  choices: [
73
- { title: "Monolith API", value: "monolith" },
74
- { title: "Microservice", value: "microservice" },
84
+ { title: pc.blue("Monolith API"), value: "monolith" },
85
+ { title: pc.magenta("Microservice"), value: "microservice" },
75
86
  ],
76
87
  },
77
88
  {
@@ -82,46 +93,46 @@ export const getProjectConfig = async () => {
82
93
  ? "select"
83
94
  : null,
84
95
  name: "mode",
85
- message: "Microservice setup",
96
+ message: pc.cyan("Microservice setup"),
86
97
  choices: [
87
- { title: "With Docker", value: "docker" },
88
- { title: "Without Docker", value: "nodocker" },
98
+ { title: pc.blue("With Docker 🐳"), value: "docker" },
99
+ { title: pc.yellow("Without Docker (PM2)"), value: "nodocker" },
89
100
  ],
90
101
  },
91
102
  {
92
103
  type: isInMicroserviceProject ? "text" : isCI ? null : "multiselect",
93
104
  name: isInMicroserviceProject ? "serviceName" : "features",
94
105
  message: isInMicroserviceProject
95
- ? "New service name (e.g., user-service, order-service)"
96
- : "Select features",
106
+ ? pc.cyan("New service name") + pc.dim(" (e.g., user-service, order-service)")
107
+ : pc.cyan("Select features"),
97
108
  choices: isInMicroserviceProject
98
109
  ? undefined
99
110
  : [
100
- { title: "CORS", value: "cors" },
101
- { title: "Rate Limiter", value: "rate-limit" },
102
- { title: "Helmet", value: "helmet" },
103
- { title: "Morgan (HTTP logger)", value: "morgan" },
111
+ { title: pc.blue("CORS"), value: "cors" },
112
+ { title: pc.yellow("Rate Limiter"), value: "rate-limit" },
113
+ { title: pc.green("Helmet"), value: "helmet" },
114
+ { title: pc.magenta("Morgan (HTTP logger)"), value: "morgan" },
104
115
  ],
105
116
  },
106
117
  {
107
118
  type: isCI ? null : "toggle",
108
119
  name: "auth",
109
120
  message: isInMicroserviceProject
110
- ? "Include authentication in this service?"
111
- : "Include authentication system?",
121
+ ? pc.cyan("Include authentication in this service?")
122
+ : pc.cyan("Include authentication system?"),
112
123
  initial: true,
113
- active: "yes",
114
- inactive: "no",
124
+ active: pc.green("yes"),
125
+ inactive: pc.red("no"),
115
126
  },
116
127
  {
117
128
  type: isInMicroserviceProject && !isCI ? "multiselect" : null,
118
129
  name: "features",
119
- message: "Select features for this service",
130
+ message: pc.cyan("Select features for this service"),
120
131
  choices: [
121
- { title: "Rate Limiter", value: "rate-limit" },
122
- { title: "Helmet", value: "helmet" },
123
- { title: "Morgan (HTTP logger)", value: "morgan" },
124
- { title: "CORS", value: "cors" },
132
+ { title: pc.yellow("Rate Limiter"), value: "rate-limit" },
133
+ { title: pc.green("Helmet"), value: "helmet" },
134
+ { title: pc.magenta("Morgan (HTTP logger)"), value: "morgan" },
135
+ { title: pc.blue("CORS"), value: "cors" },
125
136
  ],
126
137
  },
127
138
  ]);
@@ -131,6 +142,7 @@ export const getProjectConfig = async () => {
131
142
  res.features = res.features || [];
132
143
  res.auth = res.auth ?? false;
133
144
  res.mode = res.mode || "docker"; // Default to docker in CI
145
+ res.language = res.language || "typescript"; // Default to TypeScript in CI
134
146
  }
135
147
 
136
148
  // Merge CLI args with prompted responses
@@ -157,8 +169,8 @@ export const getProjectConfig = async () => {
157
169
  ? "docker"
158
170
  : "nodocker";
159
171
 
160
- console.log(`\n📁 Detected existing microservice project: ${sanitizedName}`);
161
- console.log(`Mode: ${mode}\n`);
172
+ console.log(pc.cyan(`\n📁 Detected existing microservice project: ${sanitizedName}`));
173
+ console.log(pc.dim(`Mode: ${mode}\n`));
162
174
  } else {
163
175
  sanitizedName = res.name.replace(/\s+/g, "-");
164
176
  target = path.resolve(process.cwd(), sanitizedName);
@@ -1,6 +1,7 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import prompts from "prompts";
4
+ import pc from "picocolors";
4
5
  import { execSync } from "child_process";
5
6
  import { fileURLToPath } from "url";
6
7
 
@@ -340,7 +341,7 @@ await connectDB();`
340
341
 
341
342
  // Install dependencies
342
343
  console.log(
343
- `\n📦 Installing dependencies for ${serviceName || "project"}...\n`
344
+ pc.cyan(`\n📦 Installing dependencies for ${serviceName || "project"}...\n`)
344
345
  );
345
346
 
346
347
  let installSucceeded = false;
@@ -362,23 +363,23 @@ await connectDB();`
362
363
  installSucceeded = true;
363
364
 
364
365
  // Run format after successful install
365
- console.log("\n🎨 Formatting code...\n");
366
+ console.log(pc.cyan("\n🎨 Formatting code...\n"));
366
367
  try {
367
368
  execSync("npm run format", { cwd: serviceRoot, stdio: "inherit" });
368
369
  } catch (formatError) {
369
- console.warn("⚠️ Warning: Code formatting failed. You can run it manually later with: npm run format\n");
370
+ console.warn(pc.yellow("⚠️ Warning: Code formatting failed. You can run it manually later with: npm run format\n"));
370
371
  }
371
372
  } catch (error) {
372
- console.error("\n⚠️ Warning: Some dependencies failed to install.");
373
- console.error("This is usually due to native modules (like argon2) requiring build tools.\n");
374
- console.error("💡 Solutions:");
375
- console.error(" 1. Install build tools: npm install --global windows-build-tools");
376
- console.error(" 2. Or switch to bcrypt (works better on Windows)");
377
- console.error(" 3. Or manually install later: cd " + (serviceName || res.sanitizedName) + " && npm install");
378
- console.error(" 4. Then run: npm run format\n");
373
+ console.error(pc.yellow("\n⚠️ Warning: Some dependencies failed to install."));
374
+ console.error(pc.yellow("This is usually due to native modules (like argon2) requiring build tools.\n"));
375
+ console.error(pc.cyan("💡 Solutions:"));
376
+ console.error(pc.dim(" 1. Install build tools: npm install --global windows-build-tools"));
377
+ console.error(pc.dim(" 2. Or switch to bcrypt (works better on Windows)"));
378
+ console.error(pc.dim(" 3. Or manually install later: cd " + (serviceName || res.sanitizedName) + " && npm install"));
379
+ console.error(pc.dim(" 4. Then run: npm run format\n"));
379
380
 
380
381
  // Don't exit - let the project be created anyway
381
- console.log("⏭️ Continuing with project creation...\n");
382
+ console.log(pc.cyan("⏭️ Continuing with project creation...\n"));
382
383
  }
383
384
 
384
385
  return { deps, installSucceeded };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Transform TypeScript files to JavaScript
3
+ * Removes type annotations and converts file extensions
4
+ */
5
+
6
+ /**
7
+ * Strip TypeScript type annotations from code
8
+ * @param {string} content - TypeScript file content
9
+ * @returns {string} - JavaScript content
10
+ */
11
+ export function stripTypeScript(content) {
12
+ let jsContent = content;
13
+
14
+ // Remove type imports
15
+ jsContent = jsContent.replace(/^import\s+type\s+.*?;?\s*$/gm, '');
16
+
17
+ // Remove type-only imports from regular imports
18
+ jsContent = jsContent.replace(/,\s*type\s+\{[^}]+\}/g, '');
19
+ jsContent = jsContent.replace(/import\s+\{[^}]*type\s+[^}]*\}/g, (match) => {
20
+ return match.replace(/,?\s*type\s+\w+/g, '').replace(/\{\s*,/, '{').replace(/,\s*\}/, '}');
21
+ });
22
+
23
+ // Remove interface declarations
24
+ jsContent = jsContent.replace(/^export\s+interface\s+\w+\s*\{[^}]*\}\s*$/gm, '');
25
+ jsContent = jsContent.replace(/^interface\s+\w+\s*\{[^}]*\}\s*$/gm, '');
26
+
27
+ // Remove type aliases
28
+ jsContent = jsContent.replace(/^export\s+type\s+\w+\s*=\s*[^;]+;\s*$/gm, '');
29
+ jsContent = jsContent.replace(/^type\s+\w+\s*=\s*[^;]+;\s*$/gm, '');
30
+
31
+ // Remove parameter types: (param: Type) => (param)
32
+ jsContent = jsContent.replace(/\(\s*(\w+)\s*:\s*[^,)]+/g, '($1');
33
+ jsContent = jsContent.replace(/,\s*(\w+)\s*:\s*[^,)]+/g, ', $1');
34
+
35
+ // Remove return types: ): Type => ): void => ()
36
+ jsContent = jsContent.replace(/\)\s*:\s*[^{=>\n]+\s*=>/g, ') =>');
37
+ jsContent = jsContent.replace(/\)\s*:\s*[^{=>\n]+\s*\{/g, ') {');
38
+
39
+ // Remove variable type annotations: const x: Type = ... => const x = ...
40
+ jsContent = jsContent.replace(/:\s*[^=\n]+(?=\s*=)/g, '');
41
+
42
+ // Remove function return types
43
+ jsContent = jsContent.replace(/function\s+(\w+)\s*\([^)]*\)\s*:\s*[^{]+\{/g, 'function $1($2) {');
44
+
45
+ // Remove as type assertions
46
+ jsContent = jsContent.replace(/\s+as\s+\w+/g, '');
47
+
48
+ // Remove angle bracket type assertions: <Type>value => value
49
+ jsContent = jsContent.replace(/<\w+>/g, '');
50
+
51
+ // Remove ! non-null assertions
52
+ jsContent = jsContent.replace(/(\w+)!/g, '$1');
53
+
54
+ // Remove ? optional chaining that's TypeScript specific
55
+ // Keep ?. and ?? as they're valid JS
56
+
57
+ // Remove generic type parameters
58
+ jsContent = jsContent.replace(/<[^>]+>/g, '');
59
+
60
+ // Remove enum declarations (convert to object)
61
+ jsContent = jsContent.replace(/enum\s+(\w+)\s*\{([^}]+)\}/g, (match, name, body) => {
62
+ const entries = body.split(',').map(e => e.trim()).filter(Boolean);
63
+ const obj = entries.map(e => {
64
+ const [key, value] = e.split('=').map(s => s.trim());
65
+ return value ? ` ${key}: ${value}` : ` ${key}: "${key}"`;
66
+ }).join(',\n');
67
+ return `const ${name} = {\n${obj}\n}`;
68
+ });
69
+
70
+ // Remove multiple consecutive blank lines
71
+ jsContent = jsContent.replace(/\n\s*\n\s*\n/g, '\n\n');
72
+
73
+ return jsContent;
74
+ }
75
+
76
+ /**
77
+ * Get JavaScript package.json scripts
78
+ * @returns {Object} - Scripts for JavaScript project
79
+ */
80
+ export function getJavaScriptScripts() {
81
+ return {
82
+ dev: "nodemon --exec node src/server.js",
83
+ start: "node src/server.js",
84
+ lint: 'eslint "src/**/*.js"',
85
+ format: 'prettier --write "src/**/*.{js,json}"',
86
+ "check-format": 'prettier --check "src/**/*.{js,json}"',
87
+ prepare: "husky"
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Get JavaScript dependencies (remove TS-specific ones)
93
+ * @param {Object} originalDeps - Original TypeScript dependencies
94
+ * @param {Object} originalDevDeps - Original TypeScript devDependencies
95
+ * @returns {Object} - { dependencies, devDependencies }
96
+ */
97
+ export function getJavaScriptDependencies(originalDeps, originalDevDeps) {
98
+ // Remove TypeScript-specific packages
99
+ const tsPackages = [
100
+ 'typescript',
101
+ 'ts-node-dev',
102
+ 'tsconfig-paths',
103
+ '@typescript-eslint/eslint-plugin',
104
+ '@typescript-eslint/parser'
105
+ ];
106
+
107
+ const typePackages = Object.keys(originalDevDeps).filter(pkg => pkg.startsWith('@types/'));
108
+
109
+ const devDeps = { ...originalDevDeps };
110
+
111
+ // Remove TS packages
112
+ [...tsPackages, ...typePackages].forEach(pkg => {
113
+ delete devDeps[pkg];
114
+ });
115
+
116
+ // Add nodemon for JS development
117
+ devDeps.nodemon = '^3.0.2';
118
+
119
+ return {
120
+ dependencies: { ...originalDeps },
121
+ devDependencies: devDeps
122
+ };
123
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ifecodes/backend-template",
3
- "version": "1.0.10",
4
- "description": "Production-ready Express + TypeScript backend generator with optional features and microservice support",
3
+ "version": "1.1.0",
4
+ "description": "Production-ready Express + TypeScript/JavaScript backend generator with optional features and microservice support",
5
5
  "bin": {
6
6
  "ifecodes-template": "bin/cli.js"
7
7
  },
@@ -29,6 +29,7 @@
29
29
  "template",
30
30
  "boilerplate",
31
31
  "typescript",
32
+ "javascript",
32
33
  "backend",
33
34
  "express",
34
35
  "microservice",
@@ -39,11 +40,13 @@
39
40
  "docker",
40
41
  "pm2",
41
42
  "jwt",
42
- "authentication"
43
+ "authentication",
44
+ "colored-cli"
43
45
  ],
44
46
  "license": "MIT",
45
47
  "author": "Aladetan Fortune Ifeloju (IfeCodes) <ifecodes01@gmail.com>",
46
48
  "dependencies": {
49
+ "picocolors": "^1.1.1",
47
50
  "prompts": "^2.4.2"
48
51
  },
49
52
  "engines": {