@ifecodes/backend-template 1.1.1 → 1.1.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/bin/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { execSync } from "child_process";
@@ -7,7 +7,7 @@ import pc from "picocolors";
7
7
  import { getProjectConfig } from "./lib/prompts.js";
8
8
  import { setupService } from "./lib/service-setup.js";
9
9
  import { generateReadme } from "./lib/readme-generator.js";
10
- import { stripTypeScript, getJavaScriptScripts, getJavaScriptDependencies } from "./lib/ts-to-js.js";
10
+ import { stripTypeScript, getJavaScriptScripts, getJavaScriptDependencies, transformToJavaScript, transformDirectory } from "./lib/ts-to-js.js";
11
11
  import {
12
12
  generateDockerCompose,
13
13
  generatePm2Config,
@@ -67,97 +67,6 @@ if (!isInMicroserviceProject && config.projectType === "microservice") {
67
67
  console.log(`\n${pc.cyan("🏗️ Adding service:")} ${pc.bold(servicesToCreate[0])}...\n`);
68
68
  }
69
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
- );
159
- }
160
-
161
70
  // Process services
162
71
  if (isInMicroserviceProject || config.projectType === "microservice") {
163
72
  // Create shared folder for config and utils (only once)
@@ -247,6 +156,26 @@ if (isInMicroserviceProject || config.projectType === "microservice") {
247
156
  // Store for later use
248
157
  config.allInstallsSucceeded = allInstallsSucceeded;
249
158
 
159
+ // Transform to JavaScript if selected (for microservices)
160
+ if (config.language === "javascript") {
161
+ console.log(`\n${pc.cyan("⚙️ Converting microservices to JavaScript...")}\n`);
162
+
163
+ // Transform shared folder
164
+ const sharedDir = path.join(target, "shared");
165
+ if (fs.existsSync(sharedDir)) {
166
+ transformDirectory(sharedDir);
167
+ }
168
+
169
+ // Transform each service
170
+ for (const serviceName of allServices) {
171
+ const serviceRoot = path.join(target, "services", serviceName);
172
+ console.log(pc.dim(` Transforming ${serviceName}...`));
173
+ transformToJavaScript(serviceRoot);
174
+ }
175
+
176
+ console.log(pc.green("✓ JavaScript transformation complete\n"));
177
+ }
178
+
250
179
  if (mode === "docker") {
251
180
  generateDockerCompose(target, allServices);
252
181
  copyDockerfile(target, servicesToCreate);
@@ -59,19 +59,19 @@ export const getProjectConfig = async () => {
59
59
  initial: 0,
60
60
  },
61
61
  {
62
- type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
62
+ type: isInMicroserviceProject || isCI ? null : "text",
63
63
  name: "description",
64
64
  message: pc.cyan("Project description") + pc.dim(" (optional)"),
65
65
  initial: "",
66
66
  },
67
67
  {
68
- type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
68
+ type: isInMicroserviceProject || isCI ? null : "text",
69
69
  name: "author",
70
70
  message: pc.cyan("Author") + pc.dim(" (optional)"),
71
71
  initial: "",
72
72
  },
73
73
  {
74
- type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
74
+ type: isInMicroserviceProject || isCI ? null : "text",
75
75
  name: "keywords",
76
76
  message: pc.cyan("Keywords") + pc.dim(" (comma-separated, optional)"),
77
77
  initial: "",
@@ -135,13 +135,35 @@ export const getProjectConfig = async () => {
135
135
  { title: pc.blue("CORS"), value: "cors" },
136
136
  ],
137
137
  },
138
- ]);
138
+ ],
139
+ {
140
+ onCancel: () => {
141
+ console.log(pc.yellow("\n❌ Operation cancelled by user."));
142
+ process.exit(0);
143
+ }
144
+ });
139
145
 
140
146
  // Handle cancelled prompts (user pressed Ctrl+C or closed the prompt)
141
- if (!res || !res.name) {
147
+ // Check if user cancelled the prompt (Ctrl+C) vs just didn't enter anything
148
+ if (!res) {
142
149
  console.log(pc.yellow("\n❌ Operation cancelled by user."));
143
150
  process.exit(0);
144
151
  }
152
+
153
+ // Check if critical fields are missing (indicates cancellation mid-prompt)
154
+ if (!isInMicroserviceProject && !isCI) {
155
+ // For new projects, we need language and projectType
156
+ if (!res.language || (!res.projectType && !cliProjectType)) {
157
+ console.log(pc.yellow("\n❌ Operation cancelled by user."));
158
+ process.exit(0);
159
+ }
160
+ }
161
+
162
+ // If the response is empty but we expected prompts, something went wrong
163
+ if (Object.keys(res).length === 0 && !isInMicroserviceProject && !hasCliArgs && !isCI) {
164
+ console.log(pc.red("\n❌ No inputs received. Please try again."));
165
+ process.exit(1);
166
+ }
145
167
 
146
168
  // Set defaults for CI/non-interactive mode
147
169
  if (isCI) {
@@ -161,6 +183,11 @@ export const getProjectConfig = async () => {
161
183
  res.projectType = res.projectType || "monolith";
162
184
  }
163
185
  }
186
+
187
+ // Ensure we have a project name (fallback if somehow missed)
188
+ if (!res.name && !isInMicroserviceProject) {
189
+ res.name = "my-backend";
190
+ }
164
191
 
165
192
  let sanitizedName, target, isExistingProject, mode;
166
193
 
@@ -350,6 +350,14 @@ await connectDB();`
350
350
  finalPackageJson.keywords = res.keywords.split(',').map(k => k.trim()).filter(Boolean);
351
351
  }
352
352
 
353
+ // Add Node.js native path aliasing for JavaScript projects
354
+ // This replaces TypeScript's tsconfig paths with Node's native imports field
355
+ if (res.language === 'javascript') {
356
+ finalPackageJson.imports = {
357
+ "#/*": "./src/*"
358
+ };
359
+ }
360
+
353
361
  fs.writeFileSync(
354
362
  packageJsonPath,
355
363
  JSON.stringify(finalPackageJson, null, 2) + "\n"
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  /**
7
- * Strip TypeScript type annotations from code
7
+ * Strip TypeScript type annotations from code and fix imports for JavaScript
8
8
  * @param {string} content - TypeScript file content
9
9
  * @returns {string} - JavaScript content
10
10
  */
@@ -20,42 +20,114 @@ export function stripTypeScript(content) {
20
20
  return match.replace(/,?\s*type\s+\w+/g, '').replace(/\{\s*,/, '{').replace(/,\s*\}/, '}');
21
21
  });
22
22
 
23
+ // Remove imports from express types (Request, Response, NextFunction, etc.)
24
+ jsContent = jsContent.replace(/import\s*\{\s*Request\s*,\s*Response\s*,\s*NextFunction\s*\}\s*from\s*["']express["'];?\s*\n/gm, '');
25
+ jsContent = jsContent.replace(/import\s*\{\s*Request\s*,\s*Response\s*\}\s*from\s*["']express["'];?\s*\n/gm, '');
26
+ jsContent = jsContent.replace(/import\s*\{\s*Response\s*,\s*Request\s*\}\s*from\s*["']express["'];?\s*\n/gm, '');
27
+ jsContent = jsContent.replace(/import\s*\{\s*Request\s*\}\s*from\s*["']express["'];?\s*\n/gm, '');
28
+ jsContent = jsContent.replace(/import\s*\{\s*Response\s*\}\s*from\s*["']express["'];?\s*\n/gm, '');
29
+ jsContent = jsContent.replace(/import\s*\{\s*NextFunction\s*\}\s*from\s*["']express["'];?\s*\n/gm, '');
30
+
23
31
  // 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, '');
32
+ // CRITICAL: Must have 'interface' keyword explicitly (not 'const' or other keywords)
33
+ // Single-line interfaces
34
+ jsContent = jsContent.replace(/^export\s+interface\s+\w+(\s+extends\s+[\w,\s]+)?\s*\{[^}]*\}\s*;?\s*$/gm, '');
35
+ jsContent = jsContent.replace(/^interface\s+\w+(\s+extends\s+[\w,\s]+)?\s*\{[^}]*\}\s*;?\s*$/gm, '');
36
+
37
+ // Multi-line interfaces - must have a line break after the interface name/extends before {
38
+ // This prevents matching regular object literals
39
+ jsContent = jsContent.replace(/^export\s+interface\s+\w+(\s+extends\s+[\w,\s<>]+)?\s*\{[\s\S]*?^}/gm, '');
40
+ jsContent = jsContent.replace(/^interface\s+\w+(\s+extends\s+[\w,\s<>]+)?\s*\{[\s\S]*?^}/gm, '');
26
41
 
27
42
  // Remove type aliases
28
43
  jsContent = jsContent.replace(/^export\s+type\s+\w+\s*=\s*[^;]+;\s*$/gm, '');
29
44
  jsContent = jsContent.replace(/^type\s+\w+\s*=\s*[^;]+;\s*$/gm, '');
45
+
46
+ // Remove class property type annotations: public/private/protected status: number;
47
+ // CRITICAL: Only match single-line class properties (use [^\n;=]+ instead of [^;=]+)
48
+ jsContent = jsContent.replace(/^\s*(public|private|protected)?\s+(\w+)\s*:\s*[^\n;=]+;/gm, '');
49
+
50
+ // Remove visibility modifiers and types from constructor parameters
51
+ jsContent = jsContent.replace(/constructor\s*\(\s*([^)]*)\)\s*\{/g, (match, params) => {
52
+ if (!params.trim()) return 'constructor() {';
53
+ const cleanParams = params
54
+ .split(',')
55
+ .map(p => {
56
+ // Remove visibility modifiers
57
+ let cleaned = p.replace(/^\s*(public|private|protected)\s+/, '');
58
+ // Remove type annotation (everything after :)
59
+ cleaned = cleaned.replace(/:\s*[^=,]+/, '');
60
+ return cleaned.trim();
61
+ })
62
+ .filter(p => p)
63
+ .join(', ');
64
+ return `constructor(${cleanParams}) {`;
65
+ });
30
66
 
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');
67
+ // Remove ! non-null assertions FIRST (before other transformations)
68
+ // Only remove ! that appears after identifiers, closing brackets, or closing parentheses
69
+ // Be very conservative to avoid breaking other syntax
70
+ jsContent = jsContent.replace(/([a-zA-Z0-9_\])])!/g, '$1');
34
71
 
35
- // Remove return types: ): Type => ): void => ()
36
- jsContent = jsContent.replace(/\)\s*:\s*[^{=>\n]+\s*=>/g, ') =>');
37
- jsContent = jsContent.replace(/\)\s*:\s*[^{=>\n]+\s*\{/g, ') {');
72
+ // Remove function parameter types - IMPROVED VERSION
73
+ // Handle spread parameters: ...args: Type[] => ...args
74
+ jsContent = jsContent.replace(/(\.\.\.\s*\w+)\s*:\s*[^,)]+/g, '$1');
38
75
 
39
- // Remove variable type annotations: const x: Type = ... => const x = ...
40
- jsContent = jsContent.replace(/:\s*[^=\n]+(?=\s*=)/g, '');
76
+ // Handle regular parameters with more precision
77
+ // Strategy: Clean type annotations from parameter lists while avoiding object literals in function CALLS
41
78
 
42
- // Remove function return types
43
- jsContent = jsContent.replace(/function\s+(\w+)\s*\([^)]*\)\s*:\s*[^{]+\{/g, 'function $1($2) {');
79
+ // 1. Function declarations: function name(params) {
80
+ jsContent = jsContent.replace(/function\s+\w+\s*\(([^)]*)\)/g, (match, params) => {
81
+ if (!params.includes(':')) return match;
82
+ // Skip if it has object literals (look for : followed by value, not type)
83
+ // Object literals have patterns like {key: value} while types have param: Type
84
+ const cleaned = params.split(',').map(p => p.replace(/:\s*[^,=)]+(?=[,)]|$)/, '')).join(',');
85
+ return match.replace(params, cleaned);
86
+ });
44
87
 
45
- // Remove as type assertions
46
- jsContent = jsContent.replace(/\s+as\s+\w+/g, '');
88
+ // 2. Arrow functions: (params) => or (params) => {
89
+ // Be more specific: only skip if we see actual object literal patterns {key:value}
90
+ jsContent = jsContent.replace(/\(([^)]+)\)\s*=>/g, (match, params) => {
91
+ if (!params.includes(':')) return match;
92
+
93
+ // Check if this looks like an object literal: has both { and } with content between
94
+ if (/\{[^}]+\}/.test(params)) return match;
95
+
96
+ const cleaned = params.split(',').map(p => p.replace(/:\s*[^,=)]+(?=[,)]|$)/, '')).join(',');
97
+ return `(${cleaned}) =>`;
98
+ });
47
99
 
48
- // Remove angle bracket type assertions: <Type>value => value
49
- jsContent = jsContent.replace(/<\w+>/g, '');
100
+ // 3. Method definitions in classes/objects: methodName(params) {
101
+ jsContent = jsContent.replace(/(\w+)\s*\(([^)]*)\)\s*\{/g, (match, name, params) => {
102
+ if (!params.includes(':')) return match;
103
+ // Skip if it looks like object destructuring in params
104
+ if (/\{[^}]+\}/.test(params)) return match;
105
+ const cleaned = params.split(',').map(p => p.replace(/:\s*[^,=)]+(?=[,)]|$)/, '')).join(',');
106
+ return `${name}(${cleaned}) {`;
107
+ });
108
+
109
+ // Remove return type annotations - IMPROVED VERSION
110
+ // Match ): Type => but only consume the type part
111
+ jsContent = jsContent.replace(/\)\s*:\s*[^{=>]+(\s*=>)/g, ')$1');
112
+ jsContent = jsContent.replace(/\)\s*:\s*[^{=>]+(\s*\{)/g, ')$1');
113
+
114
+ // Remove function parameter types in function declarations
115
+ jsContent = jsContent.replace(/function\s+(\w+)\s*:\s*[^=]+/g, 'function $1');
50
116
 
51
- // Remove ! non-null assertions
52
- jsContent = jsContent.replace(/(\w+)!/g, '$1');
117
+ // Remove as type assertions (including 'as const')
118
+ jsContent = jsContent.replace(/\s+as\s+(const|any|unknown|readonly|\w+)/g, '');
53
119
 
54
- // Remove ? optional chaining that's TypeScript specific
55
- // Keep ?. and ?? as they're valid JS
120
+ // Remove satisfies operators
121
+ jsContent = jsContent.replace(/\s+satisfies\s+[^;,\n]+/g, '');
56
122
 
57
- // Remove generic type parameters
58
- jsContent = jsContent.replace(/<[^>]+>/g, '');
123
+ // Remove angle bracket type assertions: <Type>value => value (but not JSX)
124
+ jsContent = jsContent.replace(/<[\w\[\]]+>(?=[^<]*[^>])/g, '');
125
+
126
+ // Remove optional property markers: property?: Type => property
127
+ jsContent = jsContent.replace(/(\w+)\?\s*:/g, '$1:');
128
+
129
+ // Remove generic type parameters from class/function declarations
130
+ jsContent = jsContent.replace(/(class|function|interface)\s+(\w+)\s*<[^>]+>/g, '$1 $2');
59
131
 
60
132
  // Remove enum declarations (convert to object)
61
133
  jsContent = jsContent.replace(/enum\s+(\w+)\s*\{([^}]+)\}/g, (match, name, body) => {
@@ -67,6 +139,30 @@ export function stripTypeScript(content) {
67
139
  return `const ${name} = {\n${obj}\n}`;
68
140
  });
69
141
 
142
+ // Fix import paths for Node.js ESM
143
+ // 1. Replace TypeScript alias @/ with Node.js native #/
144
+ jsContent = jsContent.replace(/from\s+["']@\//g, 'from "#/');
145
+ jsContent = jsContent.replace(/import\s*\(\s*["']@\//g, 'import("#/');
146
+ jsContent = jsContent.replace(/require\s*\(\s*["']@\//g, 'require("#/');
147
+
148
+ // 2. Add .js extension to #/ imports (they're now pointing to .js files)
149
+ jsContent = jsContent.replace(/from\s+["']#\/([^"']+)["']/g, (match, path) => {
150
+ // Don't add .js if it already has an extension or is a directory import
151
+ if (path.endsWith('.js') || path.endsWith('.json') || path.endsWith('/')) {
152
+ return match;
153
+ }
154
+ return `from "#/${path}.js"`;
155
+ });
156
+
157
+ // 3. Add .js extension to relative imports
158
+ jsContent = jsContent.replace(/from\s+["'](\.\.?\/[^"']+)["']/g, (match, path) => {
159
+ // Don't add .js if it already has an extension
160
+ if (path.match(/\.\w+$/)) {
161
+ return match;
162
+ }
163
+ return `from "${path}.js"`;
164
+ });
165
+
70
166
  // Remove multiple consecutive blank lines
71
167
  jsContent = jsContent.replace(/\n\s*\n\s*\n/g, '\n\n');
72
168
 
@@ -121,3 +217,126 @@ export function getJavaScriptDependencies(originalDeps, originalDevDeps) {
121
217
  devDependencies: devDeps
122
218
  };
123
219
  }
220
+
221
+ /**
222
+ * Get package.json imports field for Node.js native path aliasing
223
+ * This replaces TypeScript's tsconfig paths with Node's native imports
224
+ * @returns {Object} - imports configuration
225
+ */
226
+ export function getNodeImportsConfig() {
227
+ return {
228
+ "#/*": "./src/*"
229
+ };
230
+ }
231
+
232
+ import fs from 'fs';
233
+ import path from 'path';
234
+
235
+ /**
236
+ * Transform a single TypeScript file to JavaScript
237
+ * @param {string} filePath - Path to .ts file
238
+ */
239
+ function transformFile(filePath) {
240
+ const content = fs.readFileSync(filePath, 'utf-8');
241
+ const jsContent = stripTypeScript(content);
242
+
243
+ // Write to .js file
244
+ const jsFilePath = filePath.replace(/\.ts$/, '.js');
245
+ fs.writeFileSync(jsFilePath, jsContent);
246
+
247
+ // Remove original .ts file
248
+ fs.unlinkSync(filePath);
249
+ }
250
+
251
+ /**
252
+ * Recursively transform all .ts files in a directory to .js
253
+ * @param {string} dir - Directory path
254
+ */
255
+ export function transformDirectory(dir) {
256
+ if (!fs.existsSync(dir)) return;
257
+
258
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
259
+
260
+ for (const entry of entries) {
261
+ const fullPath = path.join(dir, entry.name);
262
+
263
+ if (entry.isDirectory()) {
264
+ // Skip node_modules and other non-source directories
265
+ if (entry.name === 'node_modules' || entry.name === '.git') {
266
+ continue;
267
+ }
268
+ transformDirectory(fullPath);
269
+ } else if (entry.isFile() && entry.name.endsWith('.ts')) {
270
+ transformFile(fullPath);
271
+ }
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Main function to transform a TypeScript project to JavaScript
277
+ * @param {string} targetDir - Root directory of the project
278
+ */
279
+ export function transformToJavaScript(targetDir) {
280
+ // Transform all .ts files to .js
281
+ const srcDir = path.join(targetDir, 'src');
282
+ if (fs.existsSync(srcDir)) {
283
+ transformDirectory(srcDir);
284
+ }
285
+
286
+ // Update package.json
287
+ const packageJsonPath = path.join(targetDir, 'package.json');
288
+ if (fs.existsSync(packageJsonPath)) {
289
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
290
+
291
+ // Get JavaScript dependencies
292
+ const { dependencies, devDependencies } = getJavaScriptDependencies(
293
+ packageJson.dependencies || {},
294
+ packageJson.devDependencies || {}
295
+ );
296
+
297
+ // Update package.json
298
+ packageJson.dependencies = dependencies;
299
+ packageJson.devDependencies = devDependencies;
300
+ packageJson.scripts = getJavaScriptScripts();
301
+
302
+ // Add Node.js native imports for path aliasing
303
+ packageJson.imports = getNodeImportsConfig();
304
+
305
+ // Ensure type: module is set
306
+ packageJson.type = 'module';
307
+
308
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
309
+ }
310
+
311
+ // Remove tsconfig.json if it exists
312
+ const tsconfigPath = path.join(targetDir, 'tsconfig.json');
313
+ if (fs.existsSync(tsconfigPath)) {
314
+ fs.unlinkSync(tsconfigPath);
315
+ }
316
+
317
+ // Update .prettierrc if it exists to use .js instead of .ts
318
+ const prettierrcPath = path.join(targetDir, '.prettierrc');
319
+ if (fs.existsSync(prettierrcPath)) {
320
+ let prettierConfig = fs.readFileSync(prettierrcPath, 'utf-8');
321
+ prettierConfig = prettierConfig.replace(/\.ts/g, '.js');
322
+ fs.writeFileSync(prettierrcPath, prettierConfig);
323
+ }
324
+
325
+ // Update eslint.config.js if it exists
326
+ const eslintConfigPath = path.join(targetDir, 'eslint.config.js');
327
+ if (fs.existsSync(eslintConfigPath)) {
328
+ let eslintConfig = fs.readFileSync(eslintConfigPath, 'utf-8');
329
+
330
+ // Remove TypeScript parser and plugin
331
+ eslintConfig = eslintConfig.replace(/@typescript-eslint\/parser/g, '');
332
+ eslintConfig = eslintConfig.replace(/@typescript-eslint\/eslint-plugin/g, '');
333
+ eslintConfig = eslintConfig.replace(/tseslint\./g, '');
334
+ eslintConfig = eslintConfig.replace(/import tseslint[^\n]+\n/g, '');
335
+
336
+ // Update file patterns
337
+ eslintConfig = eslintConfig.replace(/\*\*\/\*\.ts/g, '**/*.js');
338
+
339
+ fs.writeFileSync(eslintConfigPath, eslintConfig);
340
+ }
341
+ }
342
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifecodes/backend-template",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
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"
@@ -43,5 +43,3 @@ export class InternalServerError extends HttpError {
43
43
  super(500, message);
44
44
  }
45
45
  }
46
-
47
- export default HttpError;
@@ -1,7 +1,17 @@
1
1
  import { ENV } from "@/config";
2
2
 
3
- function format(tag: string, args: unknown[]) {
4
- return [`%c[${tag}]`, "color:#6366f1;font-weight:600", ...args];
3
+ // ANSI color codes for terminal output
4
+ const colors = {
5
+ reset: "\x1b[0m",
6
+ blue: "\x1b[34m",
7
+ cyan: "\x1b[36m",
8
+ yellow: "\x1b[33m",
9
+ red: "\x1b[31m",
10
+ bold: "\x1b[1m",
11
+ };
12
+
13
+ function format(tag: string, color: string) {
14
+ return `${color}${colors.bold}[${tag}]${colors.reset}`;
5
15
  }
6
16
 
7
17
  function getEnvironment() {
@@ -13,22 +23,22 @@ function getEnvironment() {
13
23
  const logger = {
14
24
  log(tag: string, ...args: unknown[]) {
15
25
  if (getEnvironment() !== "development") return;
16
- console.log(...format(tag, args));
26
+ console.log(format(tag, colors.blue), ...args);
17
27
  },
18
28
 
19
29
  info(tag: string, ...args: unknown[]) {
20
30
  if (getEnvironment() !== "development") return;
21
- console.info(...format(tag, args));
31
+ console.info(format(tag, colors.cyan), ...args);
22
32
  },
23
33
 
24
34
  warn(tag: string, ...args: unknown[]) {
25
35
  if (getEnvironment() !== "development") return;
26
- console.warn(...format(tag, args));
36
+ console.warn(format(tag, colors.yellow), ...args);
27
37
  },
28
38
 
29
39
  error(tag: string, ...args: unknown[]) {
30
40
  if (getEnvironment() !== "development") return;
31
- console.error(...format(tag, args));
41
+ console.error(format(tag, colors.red), ...args);
32
42
  },
33
43
  };
34
44