scaffy-tool 0.2.0 → 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.
Files changed (45) hide show
  1. package/README.md +20 -25
  2. package/cli.js +14 -35
  3. package/core/detector.js +5 -31
  4. package/core/executor.js +5 -27
  5. package/core/interviewer.js +4 -11
  6. package/core/plugin-validator.js +1 -1
  7. package/core/registry.js +37 -25
  8. package/core/utils.js +2 -19
  9. package/package.json +11 -11
  10. package/registry/go/gin/plugin.json +23 -0
  11. package/registry/go/gin/v1/questions.js +19 -0
  12. package/registry/go/gin/v1/scaffold.js +196 -0
  13. package/registry/index.json +2 -2
  14. package/registry/javascript/expressjs/plugin.json +44 -0
  15. package/registry/javascript/expressjs/v4/questions.js +43 -0
  16. package/registry/javascript/expressjs/v4/scaffold.js +236 -0
  17. package/registry/javascript/nestjs/plugin.json +2 -2
  18. package/registry/javascript/nestjs/v10/questions.js +4 -2
  19. package/registry/javascript/nestjs/v10/scaffold.js +1 -1
  20. package/registry/javascript/nestjs/v11/questions.js +61 -0
  21. package/registry/javascript/nestjs/v11/scaffold.js +94 -0
  22. package/registry/javascript/nextjs/plugin.json +44 -0
  23. package/registry/javascript/nextjs/v14/questions.js +44 -0
  24. package/registry/javascript/nextjs/v14/scaffold.js +57 -0
  25. package/registry/javascript/vuejs/v3/questions.js +4 -2
  26. package/registry/javascript/vuejs/v3/scaffold.js +3 -10
  27. package/registry/php/laravel/plugin.json +2 -2
  28. package/registry/php/laravel/v11/questions.js +1 -1
  29. package/registry/php/laravel/v11/scaffold.js +1 -1
  30. package/registry/php/laravel/v12/questions.js +45 -0
  31. package/registry/php/laravel/v12/scaffold.js +46 -0
  32. package/registry/php/laravel/v13/questions.js +28 -0
  33. package/registry/php/laravel/v13/scaffold.js +46 -0
  34. package/registry/php/laravel/v13/test/.gitkeep +0 -0
  35. package/registry/php/laravel/v13/test/questions.test.js +48 -0
  36. package/registry/php/laravel/v13/test/scaffold.test.js +105 -0
  37. package/registry/php/symfony/plugin.json +35 -0
  38. package/registry/php/symfony/v7/questions.js +32 -0
  39. package/registry/php/symfony/v7/scaffold.js +86 -0
  40. package/registry/python/django/plugin.json +35 -0
  41. package/registry/python/django/v5/questions.js +24 -0
  42. package/registry/python/django/v5/scaffold.js +107 -0
  43. package/registry/python/fastapi/plugin.json +35 -0
  44. package/registry/python/fastapi/v1/questions.js +25 -0
  45. package/registry/python/fastapi/v1/scaffold.js +180 -0
package/core/utils.js CHANGED
@@ -1,40 +1,33 @@
1
- const chalk = require('chalk');
1
+ import chalk from 'chalk';
2
2
 
3
3
  const log = message => {
4
4
  console.log(chalk.white(` ${message}`));
5
5
  };
6
6
 
7
- // ─── Success Message ───────────────────────────────────
8
7
  const success = message => {
9
8
  console.log(chalk.green(`\n ${message}\n`));
10
9
  };
11
10
 
12
- // ─── Warning Message ───────────────────────────────────
13
11
  const warn = message => {
14
12
  console.log(chalk.yellow(` ⚠️ ${message}`));
15
13
  };
16
14
 
17
- // ─── Error Message ─────────────────────────────────────
18
15
  const error = message => {
19
16
  console.log(chalk.red(` ❌ ${message}`));
20
17
  };
21
18
 
22
- // ─── Section Title ─────────────────────────────────────
23
19
  const title = message => {
24
20
  console.log(chalk.cyan.bold(`\n ── ${message} ──\n`));
25
21
  };
26
22
 
27
- // ─── Divider Line ──────────────────────────────────────
28
23
  const divider = () => {
29
24
  console.log(chalk.gray(' ──────────────────────────────────────────'));
30
25
  };
31
26
 
32
- // ─── Step Indicator ────────────────────────────────────
33
27
  const step = (number, message) => {
34
28
  console.log(chalk.cyan(` [${number}]`) + chalk.white(` ${message}`));
35
29
  };
36
30
 
37
- // ─── Build Utils Object For Plugins ───────────────────
38
31
  const buildPluginUtils = executor => ({
39
32
  run: executor.run,
40
33
  runInProject: executor.runInProject,
@@ -50,14 +43,4 @@ const buildPluginUtils = executor => ({
50
43
  divider,
51
44
  });
52
45
 
53
- // ─── Exports ───────────────────────────────────────────
54
- module.exports = {
55
- log,
56
- success,
57
- warn,
58
- error,
59
- title,
60
- divider,
61
- step,
62
- buildPluginUtils,
63
- };
46
+ export { log, success, warn, error, title, divider, step, buildPluginUtils };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "scaffy-tool",
3
- "type": "commonjs",
4
- "version": "0.2.0",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
5
  "description": "One command. Any framework. Ready to code.",
6
6
  "author": "Md Tanvir Hossen <tanvirhossen112@gmail.com>",
7
7
  "license": "MIT",
@@ -10,7 +10,7 @@
10
10
  "scaffy": "cli.js"
11
11
  },
12
12
  "engines": {
13
- "node": ">=18.0.0"
13
+ "node": ">=20.0.0"
14
14
  },
15
15
  "keywords": [
16
16
  "scaffolding",
@@ -41,10 +41,10 @@
41
41
  },
42
42
  "scripts": {
43
43
  "start": "node cli.js",
44
- "test": "jest",
45
- "test:watch": "jest --watch",
46
- "test:coverage": "jest --coverage",
47
- "test:ci": "jest --coverage --ci --runInBand",
44
+ "test": "node --experimental-vm-modules node_modules/.bin/jest",
45
+ "test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
46
+ "test:watch": "node --experimental-vm-modules node_modules/.bin/jest --watch",
47
+ "test:ci": "node --experimental-vm-modules node_modules/.bin/jest --coverage --ci --runInBand",
48
48
  "lint": "eslint .",
49
49
  "lint:fix": "eslint . --fix",
50
50
  "format": "prettier --write .",
@@ -52,11 +52,11 @@
52
52
  "prepare": "husky install"
53
53
  },
54
54
  "dependencies": {
55
- "chalk": "^4.1.2",
55
+ "chalk": "^5.6.2",
56
56
  "commander": "^11.1.0",
57
57
  "figlet": "^1.10.0",
58
- "inquirer": "^8.2.6",
59
- "ora": "^5.4.1",
58
+ "inquirer": "^13.3.2",
59
+ "ora": "^9.3.0",
60
60
  "semver": "^7.7.4"
61
61
  },
62
62
  "devDependencies": {
@@ -73,7 +73,7 @@
73
73
  "!(jest.config).js": [
74
74
  "eslint --fix",
75
75
  "prettier --write",
76
- "jest --bail --findRelatedTests"
76
+ "node --experimental-vm-modules node_modules/.bin/jest --bail --findRelatedTests"
77
77
  ],
78
78
  "*.json": [
79
79
  "prettier --write"
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "Gin",
3
+ "alias": ["gin"],
4
+ "language": "go",
5
+ "latest": "v1",
6
+ "versions": ["v1"],
7
+ "description": "Fast and lightweight Go web framework",
8
+ "requires": [
9
+ {
10
+ "tool": "go",
11
+ "checkCommand": "go version",
12
+ "parseVersion": "go version go([0-9]+\\.[0-9]+\\.[0-9]+)",
13
+ "minVersion": "1.21.0",
14
+ "installGuide": {
15
+ "mac": "brew install go",
16
+ "linux": "sudo apt install golang-go",
17
+ "windows": "https://go.dev/dl",
18
+ "docs": "https://go.dev"
19
+ }
20
+ }
21
+ ],
22
+ "maintainer": "community"
23
+ }
@@ -0,0 +1,19 @@
1
+ export default [
2
+ {
3
+ type: 'list',
4
+ name: 'database',
5
+ message: 'Database?',
6
+ choices: [
7
+ { name: 'None', value: 'none' },
8
+ { name: 'PostgreSQL — GORM + postgres driver', value: 'postgresql' },
9
+ { name: 'MySQL — GORM + mysql driver', value: 'mysql' },
10
+ { name: 'SQLite — GORM + sqlite driver', value: 'sqlite' },
11
+ ],
12
+ },
13
+ {
14
+ type: 'confirm',
15
+ name: 'docker',
16
+ message: 'Include Docker config?',
17
+ default: false,
18
+ },
19
+ ];
@@ -0,0 +1,196 @@
1
+ export default async (answers, utils) => {
2
+ const { projectName, database, docker } = answers;
3
+
4
+ utils.title('Creating Gin v1 Project');
5
+
6
+ // ─── Step 1: Create project folder ────────────────────
7
+ utils.step(1, 'Creating project structure');
8
+ await utils.run(`mkdir ${projectName}`);
9
+
10
+ // ─── Step 2: Initialize Go module ─────────────────────
11
+ utils.step(2, 'Initializing Go module');
12
+ await utils.runInProject(projectName, `go mod init ${projectName}`);
13
+
14
+ // ─── Step 3: Install Gin ───────────────────────────────
15
+ utils.step(3, 'Installing Gin');
16
+ await utils.runInProject(projectName, 'go get github.com/gin-gonic/gin');
17
+
18
+ // ─── Step 4: Database ─────────────────────────────────
19
+ if (database !== 'none') {
20
+ utils.step(4, 'Installing database dependencies');
21
+ await utils.runInProject(projectName, 'go get gorm.io/gorm');
22
+
23
+ if (database === 'postgresql') {
24
+ await utils.runInProject(projectName, 'go get gorm.io/driver/postgres');
25
+ }
26
+
27
+ if (database === 'mysql') {
28
+ await utils.runInProject(projectName, 'go get gorm.io/driver/mysql');
29
+ }
30
+
31
+ if (database === 'sqlite') {
32
+ await utils.runInProject(projectName, 'go get gorm.io/driver/sqlite');
33
+ }
34
+ }
35
+
36
+ // ─── Step 5: Generate main.go ─────────────────────────
37
+ utils.step(5, 'Generating application files');
38
+
39
+ const mainGo = `package main
40
+
41
+ import (
42
+ "net/http"
43
+
44
+ "github.com/gin-gonic/gin"
45
+ )
46
+
47
+ func main() {
48
+ r := gin.Default()
49
+
50
+ r.GET("/", func(c *gin.Context) {
51
+ c.JSON(http.StatusOK, gin.H{
52
+ "message": "Hello from ${projectName}",
53
+ })
54
+ })
55
+
56
+ r.GET("/health", func(c *gin.Context) {
57
+ c.JSON(http.StatusOK, gin.H{
58
+ "status": "ok",
59
+ })
60
+ })
61
+
62
+ r.Run(":8080")
63
+ }
64
+ `;
65
+
66
+ await utils.createFile(`${projectName}/main.go`, mainGo);
67
+
68
+ // ─── Step 6: Generate database.go if needed ───────────
69
+ if (database !== 'none') {
70
+ const dsnMap = {
71
+ postgresql: `host=localhost user=postgres password=password dbname=${projectName} port=5432 sslmode=disable`,
72
+ mysql: `user:password@tcp(localhost:3306)/${projectName}?charset=utf8mb4&parseTime=True&loc=Local`,
73
+ sqlite: `${projectName}.db`,
74
+ };
75
+
76
+ const driverMap = {
77
+ postgresql: 'postgres',
78
+ mysql: 'mysql',
79
+ sqlite: 'sqlite',
80
+ };
81
+
82
+ const databaseGo = `package main
83
+
84
+ import (
85
+ "log"
86
+
87
+ "gorm.io/driver/${driverMap[database]}"
88
+ "gorm.io/gorm"
89
+ )
90
+
91
+ var DB *gorm.DB
92
+
93
+ func initDB() {
94
+ dsn := "${dsnMap[database]}"
95
+ db, err := gorm.Open(${driverMap[database]}.Open(dsn), &gorm.Config{})
96
+ if err != nil {
97
+ log.Fatal("Failed to connect to database:", err)
98
+ }
99
+ DB = db
100
+ }
101
+ `;
102
+ await utils.createFile(`${projectName}/database.go`, databaseGo);
103
+ }
104
+
105
+ // ─── Step 7: Generate .gitignore ──────────────────────
106
+ const gitignore = `# Binaries
107
+ ${projectName}
108
+ *.exe
109
+ *.exe~
110
+ *.dll
111
+ *.so
112
+ *.dylib
113
+
114
+ # Test binary
115
+ *.test
116
+
117
+ # Output of go coverage tool
118
+ *.out
119
+
120
+ # Environment
121
+ .env
122
+
123
+ # IDE
124
+ .vscode/
125
+ .idea/
126
+ `;
127
+ await utils.createFile(`${projectName}/.gitignore`, gitignore);
128
+
129
+ // ─── Step 8: Docker ───────────────────────────────────
130
+ if (docker) {
131
+ utils.step(6, 'Creating Docker config');
132
+
133
+ const dockerfile = `FROM golang:1.21-alpine AS builder
134
+
135
+ WORKDIR /app
136
+
137
+ COPY go.mod go.sum ./
138
+ RUN go mod download
139
+
140
+ COPY . .
141
+ RUN go build -o main .
142
+
143
+ FROM alpine:latest
144
+
145
+ WORKDIR /app
146
+ COPY --from=builder /app/main .
147
+
148
+ EXPOSE 8080
149
+ CMD ["./main"]
150
+ `;
151
+
152
+ const dockerCompose = `version: '3.8'
153
+ services:
154
+ web:
155
+ build: .
156
+ ports:
157
+ - "8080:8080"
158
+ depends_on:
159
+ - db
160
+ ${
161
+ database === 'postgresql'
162
+ ? ` db:
163
+ image: postgres:15
164
+ environment:
165
+ POSTGRES_PASSWORD: password
166
+ POSTGRES_DB: ${projectName}
167
+ ports:
168
+ - "5432:5432"`
169
+ : ''
170
+ }
171
+ ${
172
+ database === 'mysql'
173
+ ? ` db:
174
+ image: mysql:8.0
175
+ environment:
176
+ MYSQL_ROOT_PASSWORD: password
177
+ MYSQL_DATABASE: ${projectName}
178
+ ports:
179
+ - "3306:3306"`
180
+ : ''
181
+ }
182
+ `;
183
+
184
+ await utils.createFile(`${projectName}/Dockerfile`, dockerfile);
185
+ await utils.createFile(`${projectName}/docker-compose.yml`, dockerCompose);
186
+ }
187
+
188
+ // ─── Step 9: go mod tidy ──────────────────────────────
189
+ utils.step(7, 'Tidying Go modules');
190
+ await utils.runInProject(projectName, 'go mod tidy');
191
+
192
+ utils.success(`Gin v1 project "${projectName}" created successfully!`);
193
+ utils.log(` cd ${projectName}`);
194
+ utils.log(` go run main.go`);
195
+ utils.log(` Server running at http://localhost:8080`);
196
+ };
@@ -61,8 +61,8 @@
61
61
  "name": "FastAPI",
62
62
  "alias": ["fastapi"],
63
63
  "language": "python",
64
- "latest": "v0",
65
- "versions": ["v0"],
64
+ "latest": "v1",
65
+ "versions": ["v1"],
66
66
  "path": "python/fastapi"
67
67
  },
68
68
  {
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "ExpressJS",
3
+ "alias": ["express", "expressjs"],
4
+ "language": "javascript",
5
+ "latest": "v4",
6
+ "versions": ["v4"],
7
+ "description": "Fast, minimalist web framework for Node.js",
8
+ "requires": [
9
+ {
10
+ "tool": "node",
11
+ "checkCommand": "node --version",
12
+ "parseVersion": "v([0-9]+\\.[0-9]+\\.[0-9]+)",
13
+ "minVersion": "18.0.0",
14
+ "installGuide": {
15
+ "mac": "brew install node",
16
+ "linux": "sudo apt install nodejs",
17
+ "windows": "https://nodejs.org",
18
+ "docs": "https://nodejs.org"
19
+ }
20
+ },
21
+ {
22
+ "tool": "npm",
23
+ "checkCommand": "npm --version",
24
+ "parseVersion": "([0-9]+\\.[0-9]+\\.[0-9]+)",
25
+ "minVersion": "8.0.0",
26
+ "installGuide": {
27
+ "mac": "brew install node",
28
+ "linux": "sudo apt install npm",
29
+ "windows": "https://nodejs.org",
30
+ "docs": "https://docs.npmjs.com"
31
+ }
32
+ }
33
+ ],
34
+ "packageManagerQuestion": {
35
+ "tool": "packageManager",
36
+ "message": "Package manager:",
37
+ "choices": [
38
+ { "name": "npm", "value": "npm", "checkCommand": "npm --version" },
39
+ { "name": "yarn", "value": "yarn", "checkCommand": "yarn --version" },
40
+ { "name": "pnpm", "value": "pnpm", "checkCommand": "pnpm --version" }
41
+ ]
42
+ },
43
+ "maintainer": "community"
44
+ }
@@ -0,0 +1,43 @@
1
+ import { detectAvailableChoices } from '../../../../core/detector.js';
2
+ import { createRequire } from 'module';
3
+
4
+ const require = createRequire(import.meta.url);
5
+ const pluginMeta = require('../plugin.json');
6
+
7
+ export default async () => {
8
+ const availableManagers = await detectAvailableChoices(
9
+ pluginMeta.packageManagerQuestion.choices
10
+ );
11
+
12
+ return [
13
+ {
14
+ type: 'list',
15
+ name: 'packageManager',
16
+ message: pluginMeta.packageManagerQuestion.message,
17
+ choices: availableManagers,
18
+ },
19
+ {
20
+ type: 'confirm',
21
+ name: 'typescript',
22
+ message: 'Use TypeScript?',
23
+ default: false,
24
+ },
25
+ {
26
+ type: 'list',
27
+ name: 'database',
28
+ message: 'Database?',
29
+ choices: [
30
+ { name: 'None', value: 'none' },
31
+ { name: 'MongoDB — Mongoose ODM', value: 'mongodb' },
32
+ { name: 'PostgreSQL — pg driver', value: 'postgresql' },
33
+ { name: 'MySQL — mysql2 driver', value: 'mysql' },
34
+ ],
35
+ },
36
+ {
37
+ type: 'confirm',
38
+ name: 'docker',
39
+ message: 'Include Docker config?',
40
+ default: false,
41
+ },
42
+ ];
43
+ };
@@ -0,0 +1,236 @@
1
+ export default async (answers, utils) => {
2
+ const { projectName, packageManager, typescript, database, docker } = answers;
3
+
4
+ utils.title('Creating ExpressJS v4 Project');
5
+
6
+ // ─── Step 1: Create project folder ────────────────────
7
+ utils.step(1, 'Creating project structure');
8
+ await utils.run(`mkdir ${projectName}`);
9
+
10
+ // ─── Step 2: Init package.json ────────────────────────
11
+ utils.step(2, 'Initializing package.json');
12
+ await utils.runInProject(projectName, `${packageManager} init -y`);
13
+
14
+ // ─── Step 3: Install Express ──────────────────────────
15
+ utils.step(3, 'Installing Express');
16
+ await utils.runInProject(
17
+ projectName,
18
+ `${packageManager} ${packageManager === 'yarn' ? 'add' : 'install'} express`
19
+ );
20
+
21
+ // ─── Step 4: TypeScript ───────────────────────────────
22
+ if (typescript) {
23
+ utils.step(4, 'Installing TypeScript');
24
+ const installCmd =
25
+ packageManager === 'yarn'
26
+ ? 'add -D'
27
+ : packageManager === 'pnpm'
28
+ ? 'add -D'
29
+ : 'install -D';
30
+ await utils.runInProject(
31
+ projectName,
32
+ `${packageManager} ${installCmd} typescript @types/express @types/node ts-node`
33
+ );
34
+
35
+ const tsConfig = `{
36
+ "compilerOptions": {
37
+ "target": "ES2020",
38
+ "module": "commonjs",
39
+ "lib": ["ES2020"],
40
+ "outDir": "./dist",
41
+ "rootDir": "./src",
42
+ "strict": true,
43
+ "esModuleInterop": true,
44
+ "skipLibCheck": true,
45
+ "forceConsistentCasingInFileNames": true,
46
+ "resolveJsonModule": true
47
+ },
48
+ "include": ["src/**/*"],
49
+ "exclude": ["node_modules", "dist"]
50
+ }
51
+ `;
52
+ await utils.createFile(`${projectName}/tsconfig.json`, tsConfig);
53
+ }
54
+
55
+ // ─── Step 5: Database ─────────────────────────────────
56
+ if (database !== 'none') {
57
+ utils.step(5, 'Installing database dependencies');
58
+
59
+ const installCmd = packageManager === 'yarn' ? 'add' : 'install';
60
+
61
+ if (database === 'mongodb') {
62
+ await utils.runInProject(
63
+ projectName,
64
+ `${packageManager} ${installCmd} mongoose`
65
+ );
66
+ if (typescript) {
67
+ await utils.runInProject(
68
+ projectName,
69
+ `${packageManager} ${installCmd === 'add' ? 'add -D' : 'install -D'} @types/mongoose`
70
+ );
71
+ }
72
+ }
73
+
74
+ if (database === 'postgresql') {
75
+ await utils.runInProject(
76
+ projectName,
77
+ `${packageManager} ${installCmd} pg`
78
+ );
79
+ if (typescript) {
80
+ await utils.runInProject(
81
+ projectName,
82
+ `${packageManager} ${installCmd === 'add' ? 'add -D' : 'install -D'} @types/pg`
83
+ );
84
+ }
85
+ }
86
+
87
+ if (database === 'mysql') {
88
+ await utils.runInProject(
89
+ projectName,
90
+ `${packageManager} ${installCmd} mysql2`
91
+ );
92
+ }
93
+ }
94
+
95
+ // ─── Step 6: Generate entry point ─────────────────────
96
+ utils.step(6, 'Generating application files');
97
+
98
+ const srcDir = typescript ? `${projectName}/src` : projectName;
99
+ const entryFile = typescript ? 'index.ts' : 'index.js';
100
+
101
+ if (typescript) {
102
+ await utils.run(`mkdir ${projectName}/src`);
103
+ }
104
+
105
+ const indexContent = typescript
106
+ ? `import express, { Request, Response } from 'express'
107
+
108
+ const app = express()
109
+ const PORT = process.env.PORT || 3000
110
+
111
+ app.use(express.json())
112
+ app.use(express.urlencoded({ extended: true }))
113
+
114
+ app.get('/', (req: Request, res: Response) => {
115
+ res.json({ message: 'Hello from ${projectName}' })
116
+ })
117
+
118
+ app.get('/health', (req: Request, res: Response) => {
119
+ res.json({ status: 'ok' })
120
+ })
121
+
122
+ app.listen(PORT, () => {
123
+ console.log(\`Server running on http://localhost:\${PORT}\`)
124
+ })
125
+
126
+ export default app
127
+ `
128
+ : `import express from 'express'
129
+
130
+ const app = express()
131
+ const PORT = process.env.PORT || 3000
132
+
133
+ app.use(express.json())
134
+ app.use(express.urlencoded({ extended: true }))
135
+
136
+ app.get('/', (req, res) => {
137
+ res.json({ message: 'Hello from ${projectName}' })
138
+ })
139
+
140
+ app.get('/health', (req, res) => {
141
+ res.json({ status: 'ok' })
142
+ })
143
+
144
+ app.listen(PORT, () => {
145
+ console.log(\`Server running on http://localhost:\${PORT}\`)
146
+ })
147
+
148
+ export default app
149
+ `;
150
+
151
+ await utils.createFile(`${srcDir}/${entryFile}`, indexContent);
152
+
153
+ // ─── Step 7: .env ─────────────────────────────────────
154
+ const envContent = `PORT=3000
155
+ NODE_ENV=development
156
+ ${database === 'mongodb' ? `MONGODB_URI=mongodb://localhost:27017/${projectName}` : ''}
157
+ ${database === 'postgresql' ? `DATABASE_URL=postgresql://user:password@localhost:5432/${projectName}` : ''}
158
+ ${database === 'mysql' ? `DATABASE_URL=mysql://user:password@localhost:3306/${projectName}` : ''}
159
+ `;
160
+ await utils.createFile(`${projectName}/.env`, envContent);
161
+ await utils.createFile(`${projectName}/.env.example`, envContent);
162
+
163
+ // ─── Step 8: .gitignore ───────────────────────────────
164
+ const gitignore = `node_modules/
165
+ dist/
166
+ .env
167
+ *.log
168
+ `;
169
+ await utils.createFile(`${projectName}/.gitignore`, gitignore);
170
+
171
+ // ─── Step 9: Docker ───────────────────────────────────
172
+ if (docker) {
173
+ utils.step(7, 'Creating Docker config');
174
+
175
+ const dockerfile = `FROM node:18-alpine
176
+
177
+ WORKDIR /app
178
+
179
+ COPY package*.json ./
180
+ RUN ${packageManager} install
181
+
182
+ COPY . .
183
+ ${typescript ? 'RUN npm run build\n' : ''}
184
+ EXPOSE 3000
185
+ CMD [${typescript ? '"node", "dist/index.js"' : '"node", "index.js"'}]
186
+ `;
187
+
188
+ const dockerCompose = `version: '3.8'
189
+ services:
190
+ web:
191
+ build: .
192
+ ports:
193
+ - "3000:3000"
194
+ env_file:
195
+ - .env
196
+ ${
197
+ database === 'mongodb'
198
+ ? ` mongo:
199
+ image: mongo:7
200
+ ports:
201
+ - "27017:27017"`
202
+ : ''
203
+ }
204
+ ${
205
+ database === 'postgresql'
206
+ ? ` db:
207
+ image: postgres:15
208
+ environment:
209
+ POSTGRES_PASSWORD: password
210
+ POSTGRES_DB: ${projectName}
211
+ ports:
212
+ - "5432:5432"`
213
+ : ''
214
+ }
215
+ ${
216
+ database === 'mysql'
217
+ ? ` db:
218
+ image: mysql:8.0
219
+ environment:
220
+ MYSQL_ROOT_PASSWORD: password
221
+ MYSQL_DATABASE: ${projectName}
222
+ ports:
223
+ - "3306:3306"`
224
+ : ''
225
+ }
226
+ `;
227
+
228
+ await utils.createFile(`${projectName}/Dockerfile`, dockerfile);
229
+ await utils.createFile(`${projectName}/docker-compose.yml`, dockerCompose);
230
+ }
231
+
232
+ utils.success(`ExpressJS v4 project "${projectName}" created successfully!`);
233
+ utils.log(` cd ${projectName}`);
234
+ utils.log(` ${typescript ? 'npx ts-node src/index.ts' : 'node index.js'}`);
235
+ utils.log(` Server running at http://localhost:3000`);
236
+ };