nodejs-quickstart-structure 1.4.3 → 1.6.1

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 (30) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +14 -18
  3. package/docs/generateCase.md +23 -7
  4. package/docs/generatorFlow.md +5 -1
  5. package/docs/releaseNoteRule.md +42 -0
  6. package/lib/generator.js +41 -316
  7. package/lib/modules/app-setup.js +91 -0
  8. package/lib/modules/config-files.js +88 -0
  9. package/lib/modules/database-setup.js +99 -0
  10. package/lib/modules/kafka-setup.js +112 -0
  11. package/lib/modules/project-setup.js +31 -0
  12. package/lib/prompts.js +3 -3
  13. package/package.json +4 -3
  14. package/templates/clean-architecture/js/src/index.js.ejs +19 -6
  15. package/templates/clean-architecture/js/src/infrastructure/log/logger.js +16 -2
  16. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +18 -6
  17. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +2 -0
  18. package/templates/clean-architecture/ts/src/index.ts.ejs +27 -19
  19. package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +16 -2
  20. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +17 -6
  21. package/templates/common/Dockerfile +3 -0
  22. package/templates/common/docker-compose.yml.ejs +47 -24
  23. package/templates/common/package.json.ejs +9 -2
  24. package/templates/mvc/js/src/controllers/userController.js.ejs +13 -3
  25. package/templates/mvc/js/src/index.js.ejs +26 -17
  26. package/templates/mvc/js/src/utils/logger.js +16 -6
  27. package/templates/mvc/ts/src/controllers/userController.ts.ejs +13 -3
  28. package/templates/mvc/ts/src/index.ts.ejs +27 -18
  29. package/templates/mvc/ts/src/utils/logger.ts +16 -2
  30. package/templates/mvc/js/src/config/database.js +0 -12
package/CHANGELOG.md ADDED
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.6.1] - 2026-02-11
9
+
10
+ ### Fixed
11
+ - Included `CHANGELOG.md` in the published npm package so users can see version history.
12
+ - Updated `README.md` with details on 64+ supported project combinations.
13
+
14
+ ## [1.6.0] - 2026-02-10
15
+
16
+ ### Refactored
17
+ - Modularized `lib/generator.js` into distinct modules (`lib/modules/`) for better maintainability.
18
+ - Extracted logic for project setup, config files, database, app setup, and Kafka.
19
+
20
+ ### Fixed
21
+ - Resolved syntax error in `UserRepository.js` for Clean Architecture projects with 'None' database.
22
+ - Increased health check timeout to 60s in validation scripts to prevent false positives in heavy environments (MySQL + Kafka).
23
+ - Fixed missing `src/views` copy logic for MVC projects, resolving Docker build errors.
24
+ - Corrected CLI argument parsing to properly exclude undefined flags (e.g., `--db-name`).
25
+ - Removed obsolete `version: '3.8'` from `docker-compose.yml`.
26
+
27
+ ## [1.5.0] - 2026-02-10
28
+
29
+ ### Added
30
+ - Implemented structured logging with `winston-daily-rotate-file` (14-day retention, daily rotation).
31
+ - Added HTTP request logging using `morgan` middleware.
32
+
33
+ ### Fixed
34
+ - Resolved `EACCES` permission errors for log directories in Docker.
35
+
36
+ ## [1.4.5] - 2026-02-10
37
+
38
+ ### Changed
39
+ - Bumped version to 1.4.5.
40
+
41
+ ## [1.4.4] - 2026-02-10
42
+
43
+ ### Added
44
+ - Created `CHANGELOG.md` to track release history.
45
+
46
+ ## [1.4.3] - 2026-02-10
47
+
48
+ ### Fixed
49
+ - Updated CLI help text description to include MongoDB support.
50
+ - Fixed unexpected character issues in npm scripts.
51
+ - General bug fixes and improvements.
52
+
53
+ ## [1.0.0] - 2026-02-03
54
+
55
+ ### Added
56
+ - Initial release of `nodejs-quickstart-structure`.
57
+ - Scaffolding for MVC and Clean Architecture.
58
+ - Support for Express.js.
59
+ - Database integration (MongoDB, MySQL, PostgreSQL).
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A powerful CLI tool to scaffold production-ready Node.js microservices with built-in best practices, allowing you to choose between **MVC** or **Clean Architecture**, **JavaScript** or **TypeScript**, and your preferred database.
4
4
 
5
+ [![Medium Article](https://img.shields.io/badge/Medium-Read%20Article-black?logo=medium)](https://medium.com/@paudang/nodejs-quickstart-generator-93c276d60e0b)
6
+
5
7
  ![Demo](docs/demo.gif)
6
8
 
7
9
  ## Features
@@ -26,6 +28,18 @@ We don't just generate boilerplate; we generate **production-ready** foundations
26
28
  - **⚓ Git Hooks**: `Husky` and `Lint-Staged` to ensure no bad code is ever committed.
27
29
  - **🐳 DevOps**: Highly optimized **Multi-Stage Dockerfile** for small, secure production images.
28
30
 
31
+ ## 🧩 64+ Project Combinations
32
+
33
+ The CLI supports a massive number of configurations to fit your exact needs:
34
+
35
+ - **64 Core Combinations**:
36
+ - **MVC Architecture**: 48 variants (Languages × View Engines × Databases × Communication Patterns)
37
+ - **Clean Architecture**: 16 variants (Languages × Databases × Communication Patterns)
38
+ - **128 Total Scenarios**:
39
+ - Every combination can be generated with or without **GitHub Actions CI/CD**, doubling the possibilities.
40
+
41
+ For a detailed list of all supported cases, check out [docs/generateCase.md](docs/generateCase.md).
42
+
29
43
  ## Installation
30
44
 
31
45
  You can install the tool globally directly from npm:
@@ -34,24 +48,6 @@ You can install the tool globally directly from npm:
34
48
  npm install -g nodejs-quickstart-structure
35
49
  ```
36
50
 
37
- ### Manual Installation (For Development)
38
-
39
- If you want to modify the CLI itself:
40
-
41
- 1. Clone this repository:
42
- ```bash
43
- git clone https://github.com/paudang/nodejs-quickstart-structure.git
44
- cd nodejs-quickstart-structure
45
- ```
46
- 2. Install dependencies:
47
- ```bash
48
- npm install
49
- ```
50
- 3. Link globally:
51
- ```bash
52
- npm link
53
- ```
54
-
55
51
  ## Usage
56
52
 
57
53
  Once installed, simply run the following command in any directory where you want to create a new project:
@@ -3,12 +3,12 @@
3
3
  This document lists the **48 possible project combinations** supported by the `nodejs-quickstart` CLI. These combinations cover all supported languages, architectures, databases (including MongoDB), and communication patterns.
4
4
 
5
5
  ## Summary
6
- - **MVC Architecture**: 36 Combinations
7
- - (2 Languages × 3 View Engines × 3 Databases × 2 Patterns)
8
- - **Clean Architecture**: 12 Combinations
9
- - (2 Languages × 1 View Engine (None) × 3 Databases × 2 Patterns)
6
+ - **MVC Architecture**: 48 Combinations
7
+ - (2 Languages × 3 View Engines × 4 Databases (incl. None) × 2 Patterns)
8
+ - **Clean Architecture**: 16 Combinations
9
+ - (2 Languages × 1 View Engine (None) × 4 Databases (incl. None) × 2 Patterns)
10
10
 
11
- **Total Core Combinations: 48**
11
+ **Total Core Combinations: 64**
12
12
 
13
13
  > **Note on CI/CD**: Each of these 48 combinations can be generated with or without the **GitHub Actions CI Workflow** (`--include-ci`). This effectively creates **96 possible project states**. The validation script currently defaults to *including* CI to verify the full "Professional Standards" feature set.
14
14
 
@@ -54,13 +54,25 @@ This document lists the **48 possible project combinations** supported by the `n
54
54
  | 34 | TypeScript | MVC | Pug | PostgreSQL | Kafka |
55
55
  | 35 | TypeScript | MVC | Pug | MongoDB | REST APIs |
56
56
  | 36 | TypeScript | MVC | Pug | MongoDB | Kafka |
57
+ | 37 | JavaScript | MVC | None | None | REST APIs |
58
+ | 38 | JavaScript | MVC | None | None | Kafka |
59
+ | 39 | JavaScript | MVC | EJS | None | REST APIs |
60
+ | 40 | JavaScript | MVC | EJS | None | Kafka |
61
+ | 41 | JavaScript | MVC | Pug | None | REST APIs |
62
+ | 42 | JavaScript | MVC | Pug | None | Kafka |
63
+ | 43 | TypeScript | MVC | None | None | REST APIs |
64
+ | 44 | TypeScript | MVC | None | None | Kafka |
65
+ | 45 | TypeScript | MVC | EJS | None | REST APIs |
66
+ | 46 | TypeScript | MVC | EJS | None | Kafka |
67
+ | 47 | TypeScript | MVC | Pug | None | REST APIs |
68
+ | 48 | TypeScript | MVC | Pug | None | Kafka |
57
69
 
58
- ## 2. Clean Architecture (12 Cases)
70
+ ## 2. Clean Architecture (16 Cases)
59
71
  *Note: Clean Architecture does not use server-side view engines (EJS/Pug).*
60
72
 
61
73
  | # | Language | Architecture | View Engine | Database | Communication |
62
74
  | :--- | :--- | :--- | :--- | :--- | :--- |
63
- | 37 | JavaScript | Clean Architecture | N/A | MySQL | REST APIs |
75
+ | 1 | JavaScript | Clean Architecture | N/A | MySQL | REST APIs |
64
76
  | 38 | JavaScript | Clean Architecture | N/A | MySQL | Kafka |
65
77
  | 39 | JavaScript | Clean Architecture | N/A | PostgreSQL | REST APIs |
66
78
  | 40 | JavaScript | Clean Architecture | N/A | PostgreSQL | Kafka |
@@ -72,3 +84,7 @@ This document lists the **48 possible project combinations** supported by the `n
72
84
  | 46 | TypeScript | Clean Architecture | N/A | PostgreSQL | Kafka |
73
85
  | 47 | TypeScript | Clean Architecture | N/A | MongoDB | REST APIs |
74
86
  | 48 | TypeScript | Clean Architecture | N/A | MongoDB | Kafka |
87
+ | 49 | JavaScript | Clean Architecture | N/A | None | REST APIs |
88
+ | 50 | JavaScript | Clean Architecture | N/A | None | Kafka |
89
+ | 51 | TypeScript | Clean Architecture | N/A | None | REST APIs |
90
+ | 52 | TypeScript | Clean Architecture | N/A | None | Kafka |
@@ -23,7 +23,7 @@ The generator prompts the user for the following configurations. These determine
23
23
  | **Language** | `JavaScript`, `TypeScript` | `TypeScript` | The programming language to use. |
24
24
  | **Architecture** | `MVC`, `Clean Architecture` | `MVC` | The architectural pattern. |
25
25
  | **View Engine** | `None`, `EJS`, `Pug` | `None` | (MVC Only) Template engine for server-side rendering. |
26
- | **Database** | `MySQL`, `PostgreSQL`, `MongoDB` | `MySQL` | The primary database. |
26
+ | **Database** | `None`, `MySQL`, `PostgreSQL`, `MongoDB` | `None` | The primary database. |
27
27
  | **Database Name** | Input String | `demo` | The name of the database to use/create. |
28
28
  | **Communication**| `REST APIs`, `Kafka` | `REST APIs` | The primary communication method. |
29
29
  | **CI/CD Provider**| `None`, `GitHub Actions`, `Jenkins`| `None` | Setup for Continuous Integration/Deployment. |
@@ -42,6 +42,7 @@ The `generateProject` function in `lib/generator.js` executes the following step
42
42
  4. **Render `docker-compose.yml`**:
43
43
  * Uses `templates/common/docker-compose.yml.ejs`.
44
44
  * Configures services (DB, Zookeeper/Kafka) based on selection.
45
+ * **Note**: If Database is `None`, DB services are excluded.
45
46
  5. **Render `README.md`**:
46
47
  * Generates custom documentation specific to the selected stack.
47
48
  6. **Render `src/index.{js|ts}`**:
@@ -65,11 +66,14 @@ The `generateProject` function in `lib/generator.js` executes the following step
65
66
  10. **Database Setup**:
66
67
  * **MongoDB**: Sets up `migrate-mongo-config.js` and initial migration script.
67
68
  * **SQL (MySQL/Postgres)**: Sets up `flyway/sql` directory and copies initial SQL migration files.
69
+ * **None**: Skips migration setup.
68
70
  11. **Database Connection Config**:
69
71
  * Renders `database.{js|ts}` or `mongoose.{js|ts}` based on DB selection.
70
72
  * Places it in `src/config` (MVC) or `src/infrastructure/database` (Clean Arch).
73
+ * **None**: Skips this step.
71
74
  12. **Model Generation**:
72
75
  * Renders `User` model (Mongoose schema or Sequelize/TypeORM model) in the appropriate directory.
76
+ * **None**: Generates a simple Mock Entity/Model class with in-memory data for testing.
73
77
  13. **View Engine Setup (MVC)**:
74
78
  * If selected, copies views (`views/ejs` or `views/pug`) and `public` assets.
75
79
  14. **Swagger Config**:
@@ -0,0 +1,42 @@
1
+ # Release Process
2
+
3
+ This document outlines the steps to release a new version of `nodejs-quickstart-structure`.
4
+
5
+ ## 1. Update Changelog
6
+
7
+ Before releasing, update `CHANGELOG.md`:
8
+ 1. Create a new header for the new version (e.g., `## [1.4.6] - 2026-02-12`).
9
+ 2. Document new features, bug fixes, and changes.
10
+ 3. Save the file.
11
+
12
+ ## 2. Commit Changelog
13
+
14
+ Stage the `CHANGELOG.md` changes but **do not commit yet** if you want to include it in the version bump commit.
15
+ ```bash
16
+ git add CHANGELOG.md
17
+ ```
18
+
19
+ ## 3. Bump Version & Tag
20
+
21
+ Run the appropriate `npm version` command. This will assume you have a clean working directory (after staging `CHANGELOG.md`).
22
+ ```bash
23
+ npm version patch # For bug fixes (1.4.5 -> 1.4.6)
24
+ npm version minor # For new features (1.4.5 -> 1.5.0)
25
+ npm version major # For breaking changes (1.4.5 -> 2.0.0)
26
+ ```
27
+ *Note: This command automatically updates `package.json`, creates a git commit with the new version number, and creates a git tag (e.g., `v1.4.6`).*
28
+
29
+ ## 4. Push Changes & Tags
30
+
31
+ Push the new commit and the new tag to GitHub:
32
+ ```bash
33
+ git push origin main
34
+ git push origin --follow-tags
35
+ ```
36
+
37
+ ## 5. Publish to NPM (Optional)
38
+
39
+ If you are publishing this package to the npm registry:
40
+ ```bash
41
+ npm publish
42
+ ```
package/lib/generator.js CHANGED
@@ -1,342 +1,67 @@
1
- import fs from 'fs-extra';
2
1
  import path from 'path';
3
- import ejs from 'ejs';
4
2
  import { fileURLToPath } from 'url';
3
+ import { setupProjectDirectory, copyBaseStructure, copyCommonFiles } from './modules/project-setup.js';
4
+ import { renderPackageJson, renderDockerCompose, renderReadme, renderDockerfile, renderProfessionalConfig, setupCiCd, renderTestSample } from './modules/config-files.js';
5
+ import { renderIndexFile, renderDynamicComponents, renderSwaggerConfig, setupViews as setupSrcViews } from './modules/app-setup.js';
6
+ import { setupDatabase } from './modules/database-setup.js';
7
+ import { setupKafka, setupViews } from './modules/kafka-setup.js';
5
8
 
6
9
  const __filename = fileURLToPath(import.meta.url);
7
10
  const __dirname = path.dirname(__filename);
8
11
 
9
12
  export const generateProject = async (config) => {
10
- const { projectName, architecture, database, dbName, communication, language, viewEngine, ciProvider } = config;
13
+ const { projectName, architecture, language } = config;
11
14
  const targetDir = path.resolve(process.cwd(), projectName);
12
15
  const templatesDir = path.join(__dirname, '../templates');
13
16
 
14
17
  // 1. Create project directory
15
- if (await fs.pathExists(targetDir)) {
16
- throw new Error(`Directory ${projectName} already exists.`);
17
- }
18
- await fs.ensureDir(targetDir);
18
+ await setupProjectDirectory(targetDir, projectName);
19
19
 
20
- // 2. Select Structure Template
21
- const structureMap = {
22
- 'MVC': 'mvc',
23
- 'Clean Architecture': 'clean-architecture'
24
- };
25
- const archTemplate = structureMap[architecture];
26
- const langExt = language === 'TypeScript' ? 'ts' : 'js';
27
- const templatePath = path.join(templatesDir, archTemplate, langExt);
28
-
29
- // Copy base structure
30
- await fs.copy(templatePath, targetDir);
20
+ // 2. Select & Copy Base Structure
21
+ const { templatePath } = await copyBaseStructure(templatesDir, targetDir, architecture, language);
31
22
 
32
23
  // 3. Render package.json
33
- const packageJsonPath = path.join(targetDir, 'package.json');
34
- const packageTemplate = await fs.readFile(path.join(templatesDir, 'common', 'package.json.ejs'), 'utf-8');
35
- const packageContent = ejs.render(packageTemplate, {
36
- projectName,
37
- database,
38
- communication,
39
- language,
40
- viewEngine
41
- });
42
- await fs.writeFile(packageJsonPath, packageContent);
24
+ await renderPackageJson(templatesDir, targetDir, config);
43
25
 
44
26
  // 4. Render docker-compose.yml
45
- const dockerComposePath = path.join(targetDir, 'docker-compose.yml');
46
- const dockerTemplate = await fs.readFile(path.join(templatesDir, 'common', 'docker-compose.yml.ejs'), 'utf-8');
47
- const dockerContent = ejs.render(dockerTemplate, {
48
- projectName,
49
- database,
50
- dbName,
51
- communication
52
- });
53
- await fs.writeFile(dockerComposePath, dockerContent);
54
-
55
- // Render README.md
56
- const readmePath = path.join(targetDir, 'README.md');
57
- const readmeTemplate = await fs.readFile(path.join(templatesDir, 'common', 'README.md.ejs'), 'utf-8');
58
- const readmeContent = ejs.render(readmeTemplate, {
59
- projectName,
60
- architecture,
61
- database,
62
- communication,
63
- language,
64
- ciProvider
65
- });
66
- await fs.writeFile(readmePath, readmeContent);
67
-
68
- // Render index file (ts/js)
69
- const indexFileName = language === 'TypeScript' ? 'index.ts' : 'index.js';
70
- const indexPath = path.join(targetDir, 'src', indexFileName);
71
- const indexTemplateSource = path.join(templatePath, 'src', `${indexFileName}.ejs`);
72
-
73
- if (await fs.pathExists(indexTemplateSource)) {
74
- const indexTemplate = await fs.readFile(indexTemplateSource, 'utf-8');
75
- const indexContent = ejs.render(indexTemplate, {
76
- communication,
77
- viewEngine,
78
- database,
79
- architecture,
80
- projectName
81
- });
82
- await fs.writeFile(indexPath, indexContent);
83
- await fs.remove(path.join(targetDir, 'src', `${indexFileName}.ejs`));
84
- }
85
-
86
- // Render Dynamic Controllers/Repositories (User) because they depend on DB type
87
- // MVC Controller
88
- if (architecture === 'MVC') {
89
- const userControllerName = language === 'TypeScript' ? 'userController.ts' : 'userController.js';
90
- const userControllerPath = path.join(targetDir, 'src/controllers', userControllerName);
91
- const userControllerTemplate = path.join(templatePath, 'src/controllers', `${userControllerName}.ejs`);
92
-
93
- if (await fs.pathExists(userControllerTemplate)) {
94
- const content = ejs.render(await fs.readFile(userControllerTemplate, 'utf-8'), { database });
95
- await fs.writeFile(userControllerPath, content);
96
- await fs.remove(path.join(targetDir, 'src/controllers', `${userControllerName}.ejs`));
97
- }
98
- }
99
- // Clean Architecture Repo
100
- else if (architecture === 'Clean Architecture') {
101
- const repoName = language === 'TypeScript' ? 'UserRepository.ts' : 'UserRepository.js';
102
- const repoPath = path.join(targetDir, 'src/infrastructure/repositories', repoName);
103
- const repoTemplate = path.join(templatePath, 'src/infrastructure/repositories', `${repoName}.ejs`);
104
-
105
- if (await fs.pathExists(repoTemplate)) {
106
- const content = ejs.render(await fs.readFile(repoTemplate, 'utf-8'), { database });
107
- await fs.writeFile(repoPath, content);
108
- await fs.remove(path.join(targetDir, 'src/infrastructure/repositories', `${repoName}.ejs`));
109
- }
110
- }
111
- // Render Server (Clean Arch JS only)
112
- if (architecture === 'Clean Architecture' && language === 'JavaScript') {
113
- const serverName = 'server.js';
114
- const serverPath = path.join(targetDir, 'src/infrastructure/webserver', serverName);
115
- const serverTemplate = path.join(templatePath, 'src/infrastructure/webserver', `${serverName}.ejs`);
116
-
117
- if (await fs.pathExists(serverTemplate)) {
118
- const content = ejs.render(await fs.readFile(serverTemplate, 'utf-8'), { communication });
119
- await fs.writeFile(serverPath, content);
120
- await fs.remove(path.join(targetDir, 'src/infrastructure/webserver', `${serverName}.ejs`));
121
- }
122
- }
123
-
124
- // Copy Kafka files if selected
125
- if (communication === 'Kafka') {
126
- const kafkaSource = path.join(templatesDir, 'common', 'kafka', langExt);
127
- await fs.copy(kafkaSource, path.join(targetDir, 'src'));
128
-
129
- // Render Kafka Service with dynamic logger path
130
- const kafkaServiceFileName = `kafkaService.${langExt}`;
131
- const kafkaServiceTemplate = path.join(targetDir, 'src', 'services', `${kafkaServiceFileName}.ejs`);
132
-
133
- if (await fs.pathExists(kafkaServiceTemplate)) {
134
- let loggerPath = architecture === 'Clean Architecture' ? '../log/logger' : '../utils/logger';
135
- let configPath = '../config/kafka';
136
-
137
- if (language === 'TypeScript') {
138
- loggerPath = architecture === 'Clean Architecture' ? '@/infrastructure/log/logger' : '@/utils/logger';
139
- configPath = architecture === 'Clean Architecture' ? '@/infrastructure/config/kafka' : '@/config/kafka';
140
- }
141
-
142
- const content = ejs.render(await fs.readFile(kafkaServiceTemplate, 'utf-8'), { loggerPath, configPath });
143
- await fs.writeFile(path.join(targetDir, 'src', 'services', kafkaServiceFileName), content);
144
- await fs.remove(kafkaServiceTemplate);
145
- }
146
-
147
- if (architecture === 'Clean Architecture') {
148
- // Clean Architecture Restructuring
149
- await fs.ensureDir(path.join(targetDir, 'src/infrastructure/messaging'));
150
- await fs.ensureDir(path.join(targetDir, 'src/infrastructure/config'));
151
-
152
- const serviceExt = language === 'TypeScript' ? 'ts' : 'js';
153
-
154
- // Move Service to Infrastructure/Messaging
155
- await fs.move(
156
- path.join(targetDir, `src/services/kafkaService.${serviceExt}`),
157
- path.join(targetDir, `src/infrastructure/messaging/kafkaClient.${serviceExt}`),
158
- { overwrite: true }
159
- );
160
-
161
- // Move Config to Infrastructure/Config
162
- await fs.move(
163
- path.join(targetDir, `src/config/kafka.${serviceExt}`),
164
- path.join(targetDir, `src/infrastructure/config/kafka.${serviceExt}`),
165
- { overwrite: true }
166
- );
167
-
168
- // Cleanup old folders
169
- await fs.remove(path.join(targetDir, 'src/services'));
170
- // Only remove src/config if empty? But src/config came from kafka copy.
171
- // However, other parts might use src/config?
172
- // In Clean Arch, config is usually in infrastructure?
173
- // Only Kafka adds src/config. Base template doesn't have src/config for Clean Arch (it uses src/infrastructure/database).
174
- await fs.remove(path.join(targetDir, 'src/config'));
175
-
176
- // Remove REST-specific folders (Interfaces)
177
- await fs.remove(path.join(targetDir, 'src/interfaces/routes'));
178
- await fs.remove(path.join(targetDir, 'src/interfaces/controllers'));
179
- } else if (architecture === 'MVC' && (!viewEngine || viewEngine === 'None')) {
180
- // MVC Cleanup
181
- await fs.remove(path.join(targetDir, 'src/controllers'));
182
- await fs.remove(path.join(targetDir, 'src/routes'));
183
- }
184
- }
185
-
186
- // 5. Copy Common Files (.gitignore, Dockerfile, etc.)
187
- await fs.copy(path.join(templatesDir, 'common', '_gitignore'), path.join(targetDir, '.gitignore'));
188
- await fs.copy(path.join(templatesDir, 'common', '.dockerignore'), path.join(targetDir, '.dockerignore'));
189
- // await fs.copy(path.join(templatesDir, 'common', 'Dockerfile'), path.join(targetDir, 'Dockerfile'));
190
- const dockerfileTemplate = await fs.readFile(path.join(templatesDir, 'common', 'Dockerfile'), 'utf-8');
191
- const dockerfileContent = ejs.render(dockerfileTemplate, {
192
- language,
193
- viewEngine
194
- });
195
- await fs.writeFile(path.join(targetDir, 'Dockerfile'), dockerfileContent);
196
-
197
- if (language === 'TypeScript') {
198
- await fs.copy(path.join(templatesDir, 'common', 'tsconfig.json'), path.join(targetDir, 'tsconfig.json'));
199
- }
200
-
201
- // 6. Database Migrations
202
- if (database === 'MongoDB') {
203
- // Copy migrate-mongo config
204
- const migrateConfigTemplate = await fs.readFile(path.join(templatesDir, 'common', 'migrate-mongo-config.js.ejs'), 'utf-8');
205
- const migrateConfigContent = ejs.render(migrateConfigTemplate, { dbName });
206
- await fs.writeFile(path.join(targetDir, 'migrate-mongo-config.js'), migrateConfigContent);
207
-
208
- // Setup migrations directory
209
- await fs.ensureDir(path.join(targetDir, 'migrations'));
210
-
211
- // Create initial migration file with timestamp
212
- const timestamp = new Date().toISOString().replace(/[-T:.Z]/g, '').slice(0, 14); // YYYYMMDDHHMMSS
213
- const migrationTemplate = await fs.readFile(path.join(templatesDir, 'common', 'migrations', 'init.js.ejs'), 'utf-8');
214
- await fs.writeFile(path.join(targetDir, 'migrations', `${timestamp}-initial-setup.js`), migrationTemplate);
215
-
216
- } else {
217
- // Flyway for SQL
218
- await fs.ensureDir(path.join(targetDir, 'flyway/sql'));
219
- const dbType = database === 'PostgreSQL' ? 'postgres' : 'mysql';
220
- await fs.copy(path.join(templatesDir, 'db', dbType), path.join(targetDir, 'flyway/sql'));
221
- }
222
-
223
- // 7. Database Config
224
- const dbConfigFileName = language === 'TypeScript' ? (database === 'MongoDB' ? 'mongoose.ts' : 'database.ts') : (database === 'MongoDB' ? 'mongoose.js' : 'database.js');
225
- const dbConfigTemplateSource = database === 'MongoDB'
226
- ? path.join(templatesDir, 'common', 'database', langExt, `${dbConfigFileName}.ejs`)
227
- : path.join(templatesDir, 'common', 'database', langExt, `${dbConfigFileName}.ejs`);
228
-
229
- let dbConfigTarget;
230
-
231
-
232
- // Copy configurations
233
- if (architecture === 'MVC') {
234
- // Copy Views
235
- if (viewEngine && viewEngine !== 'None') {
236
- await fs.copy(path.join(templatesDir, 'common', 'views', viewEngine.toLowerCase()), path.join(targetDir, 'src/views'));
237
- }
238
- await fs.ensureDir(path.join(targetDir, 'src/config'));
239
- dbConfigTarget = path.join(targetDir, 'src/config', database === 'MongoDB' ? (language === 'TypeScript' ? 'database.ts' : 'database.js') : dbConfigFileName);
240
- } else {
241
- // Clean Architecture
242
- await fs.ensureDir(path.join(targetDir, 'src/infrastructure/database'));
243
- dbConfigTarget = path.join(targetDir, 'src/infrastructure/database', language === 'TypeScript' ? 'database.ts' : 'database.js');
244
- }
245
-
246
- if (await fs.pathExists(dbConfigTemplateSource)) {
247
- const dbTemplate = await fs.readFile(dbConfigTemplateSource, 'utf-8');
248
- const dbContent = ejs.render(dbTemplate, { database, dbName, architecture });
249
- // Ensure consistent naming for imports in other files
250
- // For MVC, we might want to rename mongoose.js to database.js to minimize refactoring in index.js?
251
- // Actually, let's keep it consistent. If MVC, we typically call it 'database.js' in require.
252
- // So we should save it as 'database.js' even if source is mongoose.js.ejs
253
- await fs.writeFile(dbConfigTarget, dbContent);
254
- }
27
+ await renderDockerCompose(templatesDir, targetDir, config);
255
28
 
256
- // Render Models
257
- const modelFileName = language === 'TypeScript' ? 'User.ts' : 'User.js';
258
- const sourceModelName = database === 'MongoDB' ? `${modelFileName}.mongoose.ejs` : `${modelFileName}.ejs`;
259
- const modelTemplateSource = path.join(templatesDir, 'common', 'database', langExt, 'models', sourceModelName);
260
- let modelTarget;
29
+ // 5. Render README.md
30
+ await renderReadme(templatesDir, targetDir, config);
261
31
 
262
- if (architecture === 'MVC') {
263
- await fs.ensureDir(path.join(targetDir, 'src/models'));
264
- modelTarget = path.join(targetDir, 'src/models', modelFileName);
265
- } else {
266
- await fs.ensureDir(path.join(targetDir, 'src/infrastructure/database/models'));
267
- modelTarget = path.join(targetDir, 'src/infrastructure/database/models', modelFileName);
268
- }
32
+ // 6. Render index file (ts/js)
33
+ await renderIndexFile(templatePath, targetDir, config);
269
34
 
270
- if (await fs.pathExists(modelTemplateSource)) {
271
- // Models need architecture to decide import path
272
- const modelTemplate = await fs.readFile(modelTemplateSource, 'utf-8');
273
- const modelContent = ejs.render(modelTemplate, { architecture });
274
- await fs.writeFile(modelTarget, modelContent);
275
- }
35
+ // 7. Render Dynamic Components (Controllers/Repos/Server)
36
+ await renderDynamicComponents(templatePath, targetDir, config);
276
37
 
277
- // 8. View Engine (MVC)
278
- if (architecture === 'MVC' && viewEngine && viewEngine !== 'None') {
279
- const publicDir = path.join(templatesDir, 'common', 'public');
280
- if (await fs.pathExists(publicDir)) {
281
- await fs.copy(publicDir, path.join(targetDir, 'public'));
282
- }
283
- }
38
+ // 8. Kafka Setup
39
+ await setupKafka(templatesDir, targetDir, config);
284
40
 
285
- // 9. Render Swagger Config (if .ejs exists)
286
- // MVC TS
287
- const swaggerMvcTs = path.join(targetDir, 'src', 'config', 'swagger.ts.ejs');
288
- if (await fs.pathExists(swaggerMvcTs)) {
289
- if (communication === 'REST APIs') {
290
- const content = ejs.render(await fs.readFile(swaggerMvcTs, 'utf-8'), { communication });
291
- await fs.writeFile(path.join(targetDir, 'src', 'config', 'swagger.ts'), content);
292
- }
293
- await fs.remove(swaggerMvcTs);
294
- }
295
- // Clean Architecture TS
296
- const swaggerCleanTs = path.join(targetDir, 'src', 'config', 'swagger.ts.ejs');
297
- // Note: In Clean Arch, it might be in src/infrastructure/webserver or src/config depending on refactor.
298
- // Based on previous moves, we saw it in src/config for TS.
299
- if (await fs.pathExists(swaggerCleanTs)) {
300
- if (communication === 'REST APIs') {
301
- const content = ejs.render(await fs.readFile(swaggerCleanTs, 'utf-8'), { communication });
302
- await fs.writeFile(path.join(targetDir, 'src', 'config', 'swagger.ts'), content);
303
- }
304
- await fs.remove(swaggerCleanTs);
305
- }
41
+ // 9. Common Files (.gitignore, Dockerfile, tsconfig)
42
+ await copyCommonFiles(templatesDir, targetDir, language);
43
+ await renderDockerfile(templatesDir, targetDir, config);
306
44
 
307
- // 10. Copy Professional Config Files (Eslint, Prettier, Husky)
308
- const eslintTemplate = await fs.readFile(path.join(templatesDir, 'common', '.eslintrc.json.ejs'), 'utf-8');
309
- const eslintContent = ejs.render(eslintTemplate, { language });
310
- await fs.writeFile(path.join(targetDir, '.eslintrc.json'), eslintContent);
45
+ // 10. Database Setup (Migrations, Config, Models)
46
+ // Note: logic for detailed view copying is also handled nicely if we ensure setupDatabase checks correctly,
47
+ // or we can move strict view logic to setupViews.
48
+ // In strict refactor, database-setup handles the content that was in the DB block.
49
+ await setupDatabase(templatesDir, targetDir, config);
311
50
 
312
- await fs.copy(path.join(templatesDir, 'common', '.prettierrc'), path.join(targetDir, '.prettierrc'));
313
- await fs.copy(path.join(templatesDir, 'common', '.lintstagedrc'), path.join(targetDir, '.lintstagedrc'));
51
+ // 11. View Engine Public Assets (MVC)
52
+ await setupViews(templatesDir, targetDir, config);
53
+ // Copy src/views (MVC)
54
+ await setupSrcViews(templatesDir, targetDir, config);
314
55
 
315
- // 10. Copy Test Config & Samples
316
- const jestTemplate = await fs.readFile(path.join(templatesDir, 'common', 'jest.config.js.ejs'), 'utf-8');
317
- const jestContent = ejs.render(jestTemplate, { language });
318
- await fs.writeFile(path.join(targetDir, 'jest.config.js'), jestContent);
56
+ // 12. Swagger Config
57
+ await renderSwaggerConfig(targetDir, config);
319
58
 
320
- // Create tests directory
321
- await fs.ensureDir(path.join(targetDir, 'tests'));
322
- const healthTestTemplate = await fs.readFile(path.join(templatesDir, 'common', 'tests', 'health.test.ts.ejs'), 'utf-8');
323
- // For now, the sample test is simple and doesn't explicitly depend on projectName, but we render it just in case we add more dynamic content later
324
- const healthTestContent = ejs.render(healthTestTemplate, { language });
325
- const testFileName = language === 'TypeScript' ? 'health.test.ts' : 'health.test.js';
326
- await fs.writeFile(path.join(targetDir, 'tests', testFileName), healthTestContent);
59
+ // 13. Professional Config & Tests
60
+ await renderProfessionalConfig(templatesDir, targetDir, language);
61
+ await renderTestSample(templatesDir, targetDir, language);
327
62
 
328
- // 11. Copy CI/CD Config (Optional)
329
- // 11. Copy CI/CD Config
330
- if (ciProvider === 'GitHub Actions') {
331
- await fs.ensureDir(path.join(targetDir, '.github/workflows'));
332
- await fs.copy(path.join(templatesDir, 'common', '_github/workflows/ci.yml'), path.join(targetDir, '.github/workflows/ci.yml'));
333
- } else if (ciProvider === 'Jenkins') {
334
- const jenkinsTemplate = await fs.readFile(path.join(templatesDir, 'common', 'Jenkinsfile.ejs'), 'utf-8');
335
- const jenkinsContent = ejs.render(jenkinsTemplate, {
336
- projectName
337
- });
338
- await fs.writeFile(path.join(targetDir, 'Jenkinsfile'), jenkinsContent);
339
- }
63
+ // 14. CI/CD
64
+ await setupCiCd(templatesDir, targetDir, config);
340
65
 
341
66
  console.log(`
342
67
  ====================================================
@@ -346,8 +71,8 @@ export const generateProject = async (config) => {
346
71
  Project: ${projectName}
347
72
  Architecture: ${architecture}
348
73
  Language: ${language}
349
- Database: ${database}
350
- Communication: ${communication}
74
+ Database: ${config.database}
75
+ Communication: ${config.communication}
351
76
 
352
77
  ----------------------------------------------------
353
78
  ✨ High-Quality Standards Applied:
@@ -357,7 +82,7 @@ export const generateProject = async (config) => {
357
82
  ✅ Security: Helmet, CORS, Rate-Limiting added
358
83
  ✅ Testing: Jest setup for Unit/Integration tests
359
84
  ✅ Docker: Production-ready multi-stage build
360
- ${ciProvider !== 'None' ? `✅ CI/CD: ${ciProvider} Workflow ready` : '❌ CI/CD: Skipped (User preferred)'}
85
+ ${config.ciProvider !== 'None' ? `✅ CI/CD: ${config.ciProvider} Workflow ready` : '❌ CI/CD: Skipped (User preferred)'}
361
86
 
362
87
  ----------------------------------------------------
363
88
  👉 Next Steps: