@ifecodes/backend-template 1.0.8 → 1.0.10
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 +22 -0
- package/bin/lib/prompts.js +18 -0
- package/bin/lib/service-setup.js +49 -2
- package/package.json +1 -1
- package/template/base/gitignore +31 -0
- package/template/base/package.json +7 -0
- package/template/base/src/utils/logger.ts +7 -6
- package/template/features/auth/base/inject.js +2 -1
- package/template/features/auth/bcrypt/inject.js +2 -0
- package/template/features/cors/inject.js +3 -2
- package/template/features/morgan/inject.js +2 -1
package/bin/cli.js
CHANGED
|
@@ -184,6 +184,28 @@ if (!isInMicroserviceProject) {
|
|
|
184
184
|
const readmeContent = generateReadme(config);
|
|
185
185
|
fs.writeFileSync(path.join(target, "README.md"), readmeContent);
|
|
186
186
|
|
|
187
|
+
// Rename gitignore to .gitignore (npm doesn't publish .gitignore files)
|
|
188
|
+
if (config.projectType === "microservice") {
|
|
189
|
+
const servicesDir = path.join(target, "services");
|
|
190
|
+
const allServices = fs.readdirSync(servicesDir).filter((f) =>
|
|
191
|
+
fs.statSync(path.join(servicesDir, f)).isDirectory()
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
for (const service of allServices) {
|
|
195
|
+
const gitignorePath = path.join(servicesDir, service, "gitignore");
|
|
196
|
+
const dotGitignorePath = path.join(servicesDir, service, ".gitignore");
|
|
197
|
+
if (fs.existsSync(gitignorePath)) {
|
|
198
|
+
fs.renameSync(gitignorePath, dotGitignorePath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
const gitignorePath = path.join(target, "gitignore");
|
|
203
|
+
const dotGitignorePath = path.join(target, ".gitignore");
|
|
204
|
+
if (fs.existsSync(gitignorePath)) {
|
|
205
|
+
fs.renameSync(gitignorePath, dotGitignorePath);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
187
209
|
// Generate .env from .env.example for each service or root
|
|
188
210
|
console.log("📄 Setting up environment files...\n");
|
|
189
211
|
if (config.projectType === "microservice") {
|
package/bin/lib/prompts.js
CHANGED
|
@@ -47,6 +47,24 @@ export const getProjectConfig = async () => {
|
|
|
47
47
|
message: "Project name",
|
|
48
48
|
initial: "my-backend",
|
|
49
49
|
},
|
|
50
|
+
{
|
|
51
|
+
type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
|
|
52
|
+
name: "description",
|
|
53
|
+
message: "Project description (optional)",
|
|
54
|
+
initial: "",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
|
|
58
|
+
name: "author",
|
|
59
|
+
message: "Author (optional)",
|
|
60
|
+
initial: "",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
|
|
64
|
+
name: "keywords",
|
|
65
|
+
message: "Keywords (comma-separated, optional)",
|
|
66
|
+
initial: "",
|
|
67
|
+
},
|
|
50
68
|
{
|
|
51
69
|
type: isInMicroserviceProject || (hasCliArgs && cliProjectType) || isCI ? null : "select",
|
|
52
70
|
name: "projectType",
|
package/bin/lib/service-setup.js
CHANGED
|
@@ -14,6 +14,7 @@ export const setupService = async (
|
|
|
14
14
|
let imports = [];
|
|
15
15
|
let middlewares = [];
|
|
16
16
|
let deps = [];
|
|
17
|
+
let devDeps = [];
|
|
17
18
|
let v1Imports = [];
|
|
18
19
|
let v1Routes = [];
|
|
19
20
|
|
|
@@ -61,6 +62,9 @@ export const setupService = async (
|
|
|
61
62
|
imports.push(feature.imports);
|
|
62
63
|
middlewares.push(feature.middleware);
|
|
63
64
|
deps.push(...feature.deps);
|
|
65
|
+
if (feature.devDeps) {
|
|
66
|
+
devDeps.push(...feature.devDeps);
|
|
67
|
+
}
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -70,6 +74,9 @@ export const setupService = async (
|
|
|
70
74
|
"../../template/features/auth/base/inject.js"
|
|
71
75
|
);
|
|
72
76
|
deps.push(...baseAuth.deps);
|
|
77
|
+
if (baseAuth.devDeps) {
|
|
78
|
+
devDeps.push(...baseAuth.devDeps);
|
|
79
|
+
}
|
|
73
80
|
|
|
74
81
|
for (const file in baseAuth.files) {
|
|
75
82
|
const fullPath = path.join(serviceRoot, file);
|
|
@@ -103,6 +110,9 @@ export const setupService = async (
|
|
|
103
110
|
`../../template/features/auth/${algo.hasher}/inject.js`
|
|
104
111
|
);
|
|
105
112
|
deps.push(...hashFeature.deps);
|
|
113
|
+
if (hashFeature.devDeps) {
|
|
114
|
+
devDeps.push(...hashFeature.devDeps);
|
|
115
|
+
}
|
|
106
116
|
|
|
107
117
|
for (const file in hashFeature.files) {
|
|
108
118
|
const fullPath = path.join(serviceRoot, file);
|
|
@@ -291,10 +301,41 @@ await connectDB();`
|
|
|
291
301
|
// Update package.json
|
|
292
302
|
const packageJsonPath = path.join(serviceRoot, "package.json");
|
|
293
303
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
294
|
-
|
|
304
|
+
|
|
305
|
+
// Create new package.json with name at the top
|
|
306
|
+
const orderedPackageJson = {
|
|
307
|
+
name: serviceName || res.sanitizedName,
|
|
308
|
+
version: packageJson.version,
|
|
309
|
+
description: res.description || packageJson.description,
|
|
310
|
+
...packageJson,
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Remove duplicate keys that were moved to the top
|
|
314
|
+
delete orderedPackageJson.name;
|
|
315
|
+
delete orderedPackageJson.version;
|
|
316
|
+
delete orderedPackageJson.description;
|
|
317
|
+
|
|
318
|
+
// Re-add them at the top in correct order
|
|
319
|
+
const finalPackageJson = {
|
|
320
|
+
name: serviceName || res.sanitizedName,
|
|
321
|
+
version: packageJson.version,
|
|
322
|
+
description: res.description || packageJson.description,
|
|
323
|
+
...orderedPackageJson,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Add author if provided
|
|
327
|
+
if (res.author) {
|
|
328
|
+
finalPackageJson.author = res.author;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Add keywords if provided
|
|
332
|
+
if (res.keywords && res.keywords.trim()) {
|
|
333
|
+
finalPackageJson.keywords = res.keywords.split(',').map(k => k.trim()).filter(Boolean);
|
|
334
|
+
}
|
|
335
|
+
|
|
295
336
|
fs.writeFileSync(
|
|
296
337
|
packageJsonPath,
|
|
297
|
-
JSON.stringify(
|
|
338
|
+
JSON.stringify(finalPackageJson, null, 2) + "\n"
|
|
298
339
|
);
|
|
299
340
|
|
|
300
341
|
// Install dependencies
|
|
@@ -311,6 +352,12 @@ await connectDB();`
|
|
|
311
352
|
stdio: "inherit",
|
|
312
353
|
});
|
|
313
354
|
}
|
|
355
|
+
if (devDeps.length) {
|
|
356
|
+
execSync(`npm install -D ${devDeps.join(" ")}`, {
|
|
357
|
+
cwd: serviceRoot,
|
|
358
|
+
stdio: "inherit",
|
|
359
|
+
});
|
|
360
|
+
}
|
|
314
361
|
execSync("npm install", { cwd: serviceRoot, stdio: "inherit" });
|
|
315
362
|
installSucceeded = true;
|
|
316
363
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
package-lock.json
|
|
4
|
+
|
|
5
|
+
# Build output
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Environment variables
|
|
9
|
+
.env
|
|
10
|
+
.env.local
|
|
11
|
+
.env.*.local
|
|
12
|
+
|
|
13
|
+
# Logs
|
|
14
|
+
logs/
|
|
15
|
+
*.log
|
|
16
|
+
npm-debug.log*
|
|
17
|
+
yarn-debug.log*
|
|
18
|
+
|
|
19
|
+
# ESLint
|
|
20
|
+
.eslintcache
|
|
21
|
+
|
|
22
|
+
# OS files
|
|
23
|
+
.DS_Store
|
|
24
|
+
Thumbs.db
|
|
25
|
+
|
|
26
|
+
# IDE
|
|
27
|
+
.vscode/
|
|
28
|
+
.idea/
|
|
29
|
+
*.swp
|
|
30
|
+
*.swo
|
|
31
|
+
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"description": "",
|
|
4
|
+
"main": "index.js",
|
|
2
5
|
"scripts": {
|
|
3
6
|
"dev": "ts-node-dev --respawn --transpile-only -r tsconfig-paths/register src/server.ts",
|
|
4
7
|
"start": "node dist/server.js",
|
|
@@ -8,6 +11,10 @@
|
|
|
8
11
|
"check-format": "prettier --check \"src/**/*.{ts,json}\"",
|
|
9
12
|
"prepare": "husky"
|
|
10
13
|
},
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"author": "",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"type": "commonjs",
|
|
11
18
|
"dependencies": {
|
|
12
19
|
"dotenv": "^16.3.1",
|
|
13
20
|
"express": "^5.2.1",
|
|
@@ -4,29 +4,30 @@ function format(tag: string, args: unknown[]) {
|
|
|
4
4
|
return [`%c[${tag}]`, "color:#6366f1;font-weight:600", ...args];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
ENV.NODE_ENV === "development" || ENV.NODE_ENV === "staging"
|
|
7
|
+
function getEnvironment() {
|
|
8
|
+
return ENV.NODE_ENV === "development" || ENV.NODE_ENV === "staging"
|
|
9
9
|
? "development"
|
|
10
10
|
: ENV.NODE_ENV;
|
|
11
|
+
}
|
|
11
12
|
|
|
12
13
|
const logger = {
|
|
13
14
|
log(tag: string, ...args: unknown[]) {
|
|
14
|
-
if (
|
|
15
|
+
if (getEnvironment() !== "development") return;
|
|
15
16
|
console.log(...format(tag, args));
|
|
16
17
|
},
|
|
17
18
|
|
|
18
19
|
info(tag: string, ...args: unknown[]) {
|
|
19
|
-
if (
|
|
20
|
+
if (getEnvironment() !== "development") return;
|
|
20
21
|
console.info(...format(tag, args));
|
|
21
22
|
},
|
|
22
23
|
|
|
23
24
|
warn(tag: string, ...args: unknown[]) {
|
|
24
|
-
if (
|
|
25
|
+
if (getEnvironment() !== "development") return;
|
|
25
26
|
console.warn(...format(tag, args));
|
|
26
27
|
},
|
|
27
28
|
|
|
28
29
|
error(tag: string, ...args: unknown[]) {
|
|
29
|
-
if (
|
|
30
|
+
if (getEnvironment() !== "development") return;
|
|
30
31
|
console.error(...format(tag, args));
|
|
31
32
|
},
|
|
32
33
|
};
|
|
@@ -84,6 +84,7 @@ export const imports = `import { authRoutes } from "./auth";`;
|
|
|
84
84
|
|
|
85
85
|
export const middleware = `router.use("/auth", authRoutes);`;
|
|
86
86
|
|
|
87
|
-
export const deps = ["jsonwebtoken", "
|
|
87
|
+
export const deps = ["jsonwebtoken", "mongoose"];
|
|
88
|
+
export const devDeps = ["@types/jsonwebtoken"];
|
|
88
89
|
|
|
89
90
|
export const targetFile = "src/modules/v1/index.ts";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export const deps = ["cors"
|
|
2
|
-
export const
|
|
1
|
+
export const deps = ["cors"];
|
|
2
|
+
export const devDeps = ["@types/cors"];
|
|
3
|
+
export const imports = `import cors from "cors";\nimport { ENV } from "@/config";`;
|
|
3
4
|
export const middleware = `
|
|
4
5
|
const corsOptions = {
|
|
5
6
|
origin: ENV.ALLOWED_ORIGIN,
|