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.
- package/CHANGELOG.md +59 -0
- package/README.md +14 -18
- package/docs/generateCase.md +23 -7
- package/docs/generatorFlow.md +5 -1
- package/docs/releaseNoteRule.md +42 -0
- package/lib/generator.js +41 -316
- package/lib/modules/app-setup.js +91 -0
- package/lib/modules/config-files.js +88 -0
- package/lib/modules/database-setup.js +99 -0
- package/lib/modules/kafka-setup.js +112 -0
- package/lib/modules/project-setup.js +31 -0
- package/lib/prompts.js +3 -3
- package/package.json +4 -3
- package/templates/clean-architecture/js/src/index.js.ejs +19 -6
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +16 -2
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +18 -6
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +2 -0
- package/templates/clean-architecture/ts/src/index.ts.ejs +27 -19
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +16 -2
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +17 -6
- package/templates/common/Dockerfile +3 -0
- package/templates/common/docker-compose.yml.ejs +47 -24
- package/templates/common/package.json.ejs +9 -2
- package/templates/mvc/js/src/controllers/userController.js.ejs +13 -3
- package/templates/mvc/js/src/index.js.ejs +26 -17
- package/templates/mvc/js/src/utils/logger.js +16 -6
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +13 -3
- package/templates/mvc/ts/src/index.ts.ejs +27 -18
- package/templates/mvc/ts/src/utils/logger.ts +16 -2
- 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
|
+
[](https://medium.com/@paudang/nodejs-quickstart-generator-93c276d60e0b)
|
|
6
|
+
|
|
5
7
|

|
|
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:
|
package/docs/generateCase.md
CHANGED
|
@@ -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**:
|
|
7
|
-
- (2 Languages × 3 View Engines ×
|
|
8
|
-
- **Clean Architecture**:
|
|
9
|
-
- (2 Languages × 1 View Engine (None) ×
|
|
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:
|
|
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 (
|
|
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
|
-
|
|
|
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 |
|
package/docs/generatorFlow.md
CHANGED
|
@@ -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` | `
|
|
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,
|
|
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
|
-
|
|
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
|
|
21
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
257
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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
|
-
|
|
271
|
-
|
|
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.
|
|
278
|
-
|
|
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.
|
|
286
|
-
|
|
287
|
-
|
|
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.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
313
|
-
await
|
|
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
|
-
//
|
|
316
|
-
|
|
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
|
-
//
|
|
321
|
-
await
|
|
322
|
-
|
|
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
|
-
//
|
|
329
|
-
|
|
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:
|