nodejs-quickstart-structure 2.0.1 → 2.1.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.
- package/CHANGELOG.md +14 -0
- package/README.md +43 -39
- package/bin/index.js +5 -2
- package/lib/generator.js +10 -4
- package/lib/modules/app-setup.js +76 -6
- package/lib/modules/auth-setup.js +143 -0
- package/lib/modules/caching-setup.js +8 -1
- package/lib/modules/database-setup.js +2 -1
- package/lib/modules/project-setup.js +1 -0
- package/lib/prompts.js +39 -0
- package/package.json +5 -4
- package/templates/clean-architecture/js/src/domain/models/User.js +3 -1
- package/templates/clean-architecture/js/src/index.js.ejs +2 -0
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -3
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +25 -2
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +27 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +49 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.spec.js.ejs +14 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +41 -4
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +69 -4
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +38 -21
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +10 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +1 -1
- package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +15 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +4 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +34 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +3 -2
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +14 -0
- package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
- package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
- package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +55 -9
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +26 -6
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +38 -23
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +3 -2
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +1 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +1 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
- package/templates/common/.cursorrules.ejs +9 -0
- package/templates/common/.env.example.ejs +17 -10
- package/templates/common/README.md.ejs +63 -18
- package/templates/common/auth/js/controllers/authController.js.ejs +168 -0
- package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
- package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
- package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
- package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
- package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
- package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
- package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
- package/templates/common/auth/ts/controllers/authController.ts.ejs +165 -0
- package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
- package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
- package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
- package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
- package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
- package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
- package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
- package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
- package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -1
- package/templates/common/database/js/models/User.js.ejs +14 -1
- package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
- package/templates/common/database/js/models/User.spec.js.ejs +12 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
- package/templates/common/database/ts/models/User.ts.ejs +17 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
- package/templates/common/docker-compose.yml.ejs +12 -0
- package/templates/common/ecosystem.config.js.ejs +9 -3
- package/templates/common/eslint.config.mjs.ejs +3 -0
- package/templates/common/jest.config.js.ejs +11 -9
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
- package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
- package/templates/common/migrations/init.js.ejs +5 -4
- package/templates/common/package.json.ejs +8 -1
- package/templates/common/prompts/project-context.md.ejs +8 -1
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
- package/templates/common/swagger.yml.ejs +148 -0
- package/templates/common/tsconfig.eslint.json +15 -0
- package/templates/common/tsconfig.json +2 -1
- package/templates/common/views/ejs/index.ejs +264 -30
- package/templates/common/views/ejs/login.ejs.ejs +244 -0
- package/templates/common/views/ejs/signup.ejs.ejs +282 -0
- package/templates/common/views/pug/index.pug +269 -38
- package/templates/common/views/pug/login.pug.ejs +195 -0
- package/templates/common/views/pug/signup.pug.ejs +241 -0
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
- package/templates/mvc/js/src/config/env.js.ejs +12 -3
- package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
- package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
- package/templates/mvc/js/src/index.js.ejs +16 -3
- package/templates/mvc/js/src/routes/api.js.ejs +14 -0
- package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
- package/templates/mvc/js/src/utils/errorMessages.js +1 -0
- package/templates/mvc/js/src/utils/httpCodes.js +1 -0
- package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
- package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
- package/templates/mvc/ts/src/index.ts.ejs +15 -3
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
- package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
- package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
- package/templates/mvc/js/src/routes/api.js +0 -10
- package/templates/mvc/ts/src/routes/api.ts +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
2
|
+
|
|
3
|
+
## [2.1.0] - 2026-04-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Pluggable JWT Authentication**: Introduced a robust, production-ready authentication system.
|
|
7
|
+
- Supports **Access & Refresh Token Rotation** for enhanced security.
|
|
8
|
+
- Integrated **Token Revocation (Blacklisting)** via Redis or Memory Cache.
|
|
9
|
+
- Secure logout flows and theft detection for refresh tokens.
|
|
10
|
+
- Seamless integration with both REST and GraphQL protocols across all architectures.
|
|
11
|
+
- **VitePress Documentation Upgrade**: Detailed [Authentication Blueprint](https://paudang.github.io/nodejs-quickstart-structure/blueprints/authentication) and cross-feature documentation updates.
|
|
12
|
+
- **Security Hardening**: Resolved moderate vulnerabilities in `vite` and `vitepress` by implementing version overrides and dependency upgrades (VitePress v1.6.4+).
|
|
13
|
+
- **Interactive README 2.0**: Completely redesigned the project landing page with a "Choose Your Journey" dual-workflow (Web UI vs CLI) and visual action previews.
|
|
14
|
+
|
|
15
|
+
---
|
|
2
16
|
|
|
3
17
|
## [2.0.1] - 2026-04-07
|
|
4
18
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Node.js Quickstart Generator
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/nodejs-quickstart-structure)
|
|
4
|
+

|
|
4
5
|
[](https://www.npmjs.com/package/nodejs-quickstart-structure)
|
|
5
6
|
[](https://www.npmjs.com/package/nodejs-quickstart-structure)
|
|
6
7
|
[](https://opensource.org/licenses/ISC)
|
|
@@ -9,57 +10,44 @@
|
|
|
9
10
|
| **Metric** | **Insight** |
|
|
10
11
|
| :--- | :--- |
|
|
11
12
|
| 🔥 **4,000+** | Downloads on npm |
|
|
12
|
-
| 🚀 **1,
|
|
13
|
+
| 🚀 **1,500+** | Recent GitHub Clones |
|
|
13
14
|
| 🌍 **Trusted by** | Devs from **Google**, **Viblo**, and global tech teams |
|
|
14
15
|
|
|
15
16
|
---
|
|
16
17
|
|
|
17
|
-
A powerful
|
|
18
|
-
|
|
19
|
-
🎯 **[Try the Next-Gen Web UI Configurator!](https://paudang.github.io/nodejs-quickstart-structure/)**
|
|
20
|
-
*Generate your exact architecture in the browser with real-time folder simulation.*
|
|
21
|
-
|
|
22
|
-

|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 🆕 What's New in v2.0.0
|
|
27
|
-
|
|
28
|
-
The v2.0.0 release is a major leap forward, turning the generator into a **Community Standard** for Node.js development:
|
|
29
|
-
|
|
30
|
-
- **Next-Gen Web UI Configurator**: A modern, browser-based visual project simulator. Click your stack and copy the generated zero-prompt CLI command instantly! [Try it here](https://paudang.github.io/nodejs-quickstart-structure/).
|
|
31
|
-
- **🦾 AI-Native Foundation**: Built-in `.cursorrules` and Agent skills optimized for **Cursor** & AI coding assistants.
|
|
32
|
-
- **🏗️ Enterprise Clean Architecture**: High-fidelity structure for professional Microservices (TS/JS).
|
|
33
|
-
- **🛡️ Hardened Security**: Integrated Snyk & SonarCloud logic in the core templates.
|
|
34
|
-
- **⚡ Zero-Prompt Workflow**: Generate projects with a single CLI command—no more answering prompts manually.
|
|
35
|
-
|
|
36
|
-
---
|
|
18
|
+
A powerful ecosystem to scaffold production-ready Node.js microservices with built-in best practices. Choose between **MVC** or **Clean Architecture**, **JavaScript** or **TypeScript**, and your preferred tech stack in seconds.
|
|
37
19
|
|
|
38
20
|
## 📌 Table of Contents
|
|
39
21
|
|
|
40
|
-
- [🚀 Quick Start](#-
|
|
22
|
+
- [🚀 Quick Start](#-choose-your-journey)
|
|
23
|
+
- [🆕 What's New](#-whats-new-in-v21-the-authentication-release)
|
|
41
24
|
- [✨ Key Features](#-key-features)
|
|
42
25
|
- [🛡️ Professional Standards](#-professional-standards)
|
|
43
|
-
- [🧩
|
|
26
|
+
- [🧩 5,280+ Project Combinations](#-5280-project-combinations)
|
|
44
27
|
- [⚙️ Configuration Options](#-configuration-options)
|
|
45
28
|
- [🏗️ Generated Project Structure](#-generated-project-structure)
|
|
46
29
|
- [📖 Documentation](#-documentation)
|
|
47
30
|
- [🗺️ Support & Roadmap](#️-roadmap--support)
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
## 🚀 Choose Your Journey
|
|
33
|
+
|
|
34
|
+
| **Path A: Next-Gen Web UI** (Recommended ⭐️) | **Path B: Interactive CLI** |
|
|
35
|
+
| :--- | :--- |
|
|
36
|
+
| <a href="https://paudang.github.io/nodejs-quickstart-structure/#configurator"><img src="docs/public/v2-preview.png" width="100%" alt="UI Preview"></a> | <img src="docs/demo.gif" width="100%" alt="CLI Demo"> |
|
|
37
|
+
| [Try Visual Configurator →](https://paudang.github.io/nodejs-quickstart-structure/#configurator) | [See CLI Commands ↓](#-path-b-interactive-cli) |
|
|
38
|
+
| ✨ **Visual Preview**: Real-time folder simulation. | ⚡ **Fast & Direct**: Quickly scaffold in terminal. |
|
|
39
|
+
| 🛠️ **Zero-Prompt**: Paste a tailored command. | 🦾 **AI-Ready**: Generates `.cursorrules`. |
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
---
|
|
52
42
|
|
|
53
|
-
|
|
43
|
+
### 🦾 Path B: Interactive CLI
|
|
44
|
+
**Scaffold your project directly from your terminal in seconds.**
|
|
54
45
|
|
|
55
46
|
```bash
|
|
56
47
|
npx nodejs-quickstart-structure@latest init
|
|
57
48
|
```
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
If you prefer to install it globally:
|
|
62
|
-
|
|
50
|
+
*Or install globally:*
|
|
63
51
|
```bash
|
|
64
52
|
npm install -g nodejs-quickstart-structure
|
|
65
53
|
# Then run:
|
|
@@ -68,6 +56,20 @@ nodejs-quickstart init
|
|
|
68
56
|
|
|
69
57
|
---
|
|
70
58
|
|
|
59
|
+
## 🆕 What's New in v2.1 (The Authentication Release)
|
|
60
|
+
|
|
61
|
+
The v2.1.0 release is a major leap forward, turning the generator into a **Community Standard**:
|
|
62
|
+
|
|
63
|
+
- **🔐 Pluggable JWT Authentication**: Production-ready access & refresh token patterns with automatic PM2/Environment configuration.
|
|
64
|
+
- **🦾 AI-Native Foundation**: Built-in `.cursorrules` optimized for **Cursor** & AI agents—projects are "Born to be Autonomously Coded."
|
|
65
|
+
- **🖼️ Next-Gen Web UI**: A browser-based visual project simulator with real-time folder previews.
|
|
66
|
+
- **🏗️ Enterprise Clean Architecture**: High-fidelity structure for professional Microservices (TS/JS).
|
|
67
|
+
- **🛡️ Hardened Security**: Integrated Snyk & SonarCloud logic in core templates.
|
|
68
|
+
- **⚡ Zero-Prompt Workflow**: Generate projects with a single CLI command.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
|
|
71
73
|
## ✨ Key Features
|
|
72
74
|
|
|
73
75
|
- **Interactive CLI**: Smooth, guided configuration process.
|
|
@@ -76,6 +78,7 @@ nodejs-quickstart init
|
|
|
76
78
|
- **Database Ready**: Pre-configured for **MySQL**, **PostgreSQL**, or **MongoDB**.
|
|
77
79
|
- **Communication Patterns**: Supports **REST**, **GraphQL** (Apollo), and **Kafka** (Event-driven).
|
|
78
80
|
- **Multi-layer Caching**: Integrated **Redis** or built-in **Memory Cache**.
|
|
81
|
+
- **Pluggable Authentication**: Built-in **JWT** support (Refresh/Access tokens).
|
|
79
82
|
- **AI-Native Optimized**: specifically designed for **Cursor** and AI agents, including built-in `.cursorrules` and Agent Skill prompts. 🚀
|
|
80
83
|
|
|
81
84
|
---
|
|
@@ -93,15 +96,15 @@ We don't just generate boilerplate; we generate **production-ready** foundations
|
|
|
93
96
|
|
|
94
97
|
---
|
|
95
98
|
|
|
96
|
-
## 🧩
|
|
99
|
+
## 🧩 5,280+ Project Combinations
|
|
97
100
|
|
|
98
101
|
The CLI supports a massive number of configurations to fit your exact needs:
|
|
99
102
|
|
|
100
|
-
- **
|
|
101
|
-
- **MVC Architecture**:
|
|
102
|
-
- **Clean Architecture**:
|
|
103
|
-
- **
|
|
104
|
-
- Every combination can be generated across
|
|
103
|
+
- **480 Core Combinations**:
|
|
104
|
+
- **MVC Architecture**: 360 variants (Languages × View Engines × Databases × Communication Patterns × Caching × Auth)
|
|
105
|
+
- **Clean Architecture**: 120 variants (Languages × Databases × Communication Patterns × Caching × Auth)
|
|
106
|
+
- **5,280+ Total Scenarios**:
|
|
107
|
+
- Every combination can be generated across 5 CI/CD providers.
|
|
105
108
|
- Optional **Enterprise-Grade Security Hardening** doubles the scenarios.
|
|
106
109
|
- Every single scenario is verified to be compatible with our **80% Coverage Threshold** policy.
|
|
107
110
|
|
|
@@ -117,8 +120,9 @@ The CLI will guide you through:
|
|
|
117
120
|
5. **Database**: `MySQL` | `PostgreSQL` | `MongoDB`
|
|
118
121
|
6. **Communication**: `REST` | `GraphQL` | `Kafka`
|
|
119
122
|
7. **Caching**: `None` | `Redis` | `Memory Cache`
|
|
120
|
-
8. **
|
|
121
|
-
9. **
|
|
123
|
+
8. **Auth**: `None` | `JWT`
|
|
124
|
+
9. **CI/CD**: `GitHub Actions` | `Jenkins` | `GitLab CI` | `CircleCI` | `Bitbucket Pipelines`
|
|
125
|
+
10. **Security**: (Optional) Snyk & SonarCloud Hardening
|
|
122
126
|
|
|
123
127
|
---
|
|
124
128
|
|
|
@@ -146,7 +150,7 @@ Depending on your choices, the structure adapts. Here is a **TypeScript + Clean
|
|
|
146
150
|
|
|
147
151
|
## 📖 Documentation
|
|
148
152
|
|
|
149
|
-
For full guides, architecture deep-dives, and feature references, visit our **[Official Documentation Site](https://paudang.github.io/nodejs-quickstart-structure/)**.
|
|
153
|
+
For full guides, architecture deep-dives, and feature references, visit our **[Official Documentation Site](https://paudang.github.io/nodejs-quickstart-structure/guide/getting-started.html)**.
|
|
150
154
|
|
|
151
155
|
---
|
|
152
156
|
|
package/bin/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const program = new Command();
|
|
|
15
15
|
|
|
16
16
|
program
|
|
17
17
|
.name('nodejs-quickstart')
|
|
18
|
-
.description('🚀 CLI to scaffold production-ready Node.js microservices.\n\nGenerates projects with:\n- MVC or Clean Architecture\n- REST or Kafka\n- MySQL, PostgreSQL, or MongoDB\n- Docker, Flyway & Mongoose support')
|
|
18
|
+
.description('🚀 CLI to scaffold production-ready Node.js microservices.\n\nGenerates projects with:\n- MVC or Clean Architecture\n- REST, GraphQL or Kafka\n- MySQL, PostgreSQL, or MongoDB\n- Auth (None, JWT)\n- Docker, Flyway & Mongoose support')
|
|
19
19
|
.version(pkg.version, '-v, --version', 'Output the current version')
|
|
20
20
|
.addHelpText('after', `\n${chalk.yellow('Example:')}\n $ nodejs-quickstart init ${chalk.gray('# Start the interactive setup')}\n`);
|
|
21
21
|
|
|
@@ -32,7 +32,10 @@ program
|
|
|
32
32
|
.option('--ci-provider <provider>', 'CI/CD Provider (None, GitHub Actions, Jenkins, GitLab CI, Bitbucket Pipelines, CircleCI)')
|
|
33
33
|
.option('--include-security', 'Include Enterprise Security Hardening')
|
|
34
34
|
.option('--no-include-security', 'Exclude Enterprise Security Hardening')
|
|
35
|
-
.option('--caching <type>', 'Caching Layer (None/Redis)')
|
|
35
|
+
.option('--caching <type>', 'Caching Layer (None/Redis/Memory Cache)')
|
|
36
|
+
.option('--auth <modes...>', 'Authentication Modes (None, JWT)')
|
|
37
|
+
.option('--advanced-options', 'Enable Advanced Options')
|
|
38
|
+
.option('--no-advanced-options', 'Disable Advanced Options')
|
|
36
39
|
.action(async (options) => {
|
|
37
40
|
// Fix for Commander camelCase conversion
|
|
38
41
|
if (options.ciProvider) {
|
package/lib/generator.js
CHANGED
|
@@ -10,6 +10,8 @@ import { setupCaching } from './modules/caching-setup.js';
|
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
11
|
const __dirname = path.dirname(__filename);
|
|
12
12
|
|
|
13
|
+
import { setupAuth } from './modules/auth-setup.js';
|
|
14
|
+
|
|
13
15
|
export const generateProject = async (config) => {
|
|
14
16
|
// 0. Normalize configuration with defaults
|
|
15
17
|
config = {
|
|
@@ -20,9 +22,11 @@ export const generateProject = async (config) => {
|
|
|
20
22
|
communication: 'REST APIs',
|
|
21
23
|
database: 'None',
|
|
22
24
|
includeSecurity: false,
|
|
25
|
+
auth: ['None'],
|
|
23
26
|
...config
|
|
24
27
|
};
|
|
25
28
|
|
|
29
|
+
|
|
26
30
|
const { projectName, architecture, language } = config;
|
|
27
31
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
28
32
|
const templatesDir = path.join(__dirname, '../templates');
|
|
@@ -62,14 +66,15 @@ export const generateProject = async (config) => {
|
|
|
62
66
|
await renderDockerfile(templatesDir, targetDir, config);
|
|
63
67
|
|
|
64
68
|
// 10. Database Setup (Migrations, Config, Models)
|
|
65
|
-
// Note: logic for detailed view copying is also handled nicely if we ensure setupDatabase checks correctly,
|
|
66
|
-
// or we can move strict view logic to setupViews.
|
|
67
|
-
// In strict refactor, database-setup handles the content that was in the DB block.
|
|
68
69
|
await setupDatabase(templatesDir, targetDir, config);
|
|
69
70
|
|
|
71
|
+
// 10.1 Auth Setup
|
|
72
|
+
await setupAuth(templatesDir, targetDir, config);
|
|
73
|
+
|
|
70
74
|
// 10a. Caching Setup
|
|
71
75
|
await setupCaching(templatesDir, targetDir, config);
|
|
72
76
|
|
|
77
|
+
|
|
73
78
|
// 11. View Engine Public Assets (MVC)
|
|
74
79
|
await setupViews(templatesDir, targetDir, config);
|
|
75
80
|
// Copy src/views (MVC)
|
|
@@ -106,7 +111,8 @@ export const generateProject = async (config) => {
|
|
|
106
111
|
Architecture: ${architecture}
|
|
107
112
|
Language: ${language}
|
|
108
113
|
Database: ${config.database}
|
|
109
|
-
Communication: ${config.communication}${config.caching && config.caching
|
|
114
|
+
Communication: ${config.communication}${config.caching && config.caching !== 'None' ? `\n Caching: ${config.caching}` : ''}
|
|
115
|
+
Authentication: ${config.auth.join(', ')}
|
|
110
116
|
|
|
111
117
|
----------------------------------------------------
|
|
112
118
|
✨ High-Quality Standards Applied:
|
package/lib/modules/app-setup.js
CHANGED
|
@@ -104,9 +104,21 @@ export const renderDynamicComponents = async (templatePath, targetDir, config) =
|
|
|
104
104
|
await fs.ensureDir(path.join(targetDir, 'tests/unit/controllers'));
|
|
105
105
|
const content = ejs.render(await fs.readFile(userControllerSpecTemplate, 'utf-8'), { ...config });
|
|
106
106
|
await fs.writeFile(userControllerSpecPath, content);
|
|
107
|
-
await fs.remove(path.join(targetDir, '
|
|
107
|
+
await fs.remove(path.join(targetDir, 'tests/unit/controllers', `${userControllerSpecName}.ejs`));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// MVC Routes
|
|
111
|
+
const routeName = language === 'TypeScript' ? 'api.ts' : 'api.js';
|
|
112
|
+
const routePath = path.join(targetDir, 'src/routes', routeName);
|
|
113
|
+
const routeTemplate = path.join(templatePath, 'src/routes', `${routeName}.ejs`);
|
|
114
|
+
|
|
115
|
+
if (await fs.pathExists(routeTemplate)) {
|
|
116
|
+
const content = ejs.render(await fs.readFile(routeTemplate, 'utf-8'), { ...config });
|
|
117
|
+
await fs.writeFile(routePath, content);
|
|
118
|
+
await fs.remove(path.join(targetDir, 'src/routes', `${routeName}.ejs`));
|
|
108
119
|
}
|
|
109
120
|
}
|
|
121
|
+
|
|
110
122
|
// Clean Architecture Repo
|
|
111
123
|
else if (architecture === 'Clean Architecture') {
|
|
112
124
|
const repoName = language === 'TypeScript' ? 'UserRepository.ts' : 'UserRepository.js';
|
|
@@ -151,7 +163,36 @@ export const renderDynamicComponents = async (templatePath, targetDir, config) =
|
|
|
151
163
|
await fs.writeFile(controllerSpecPath, content);
|
|
152
164
|
await fs.remove(path.join(targetDir, 'src/interfaces/controllers', `${controllerSpecName}.ejs`));
|
|
153
165
|
}
|
|
166
|
+
|
|
167
|
+
// CA Routes
|
|
168
|
+
const routeName = language === 'TypeScript' ? 'userRoutes.ts' : 'api.js';
|
|
169
|
+
const routePath = path.join(targetDir, 'src/interfaces/routes', routeName);
|
|
170
|
+
const routeTemplate = path.join(templatePath, 'src/interfaces/routes', `${routeName}.ejs`);
|
|
171
|
+
|
|
172
|
+
if (await fs.pathExists(routeTemplate)) {
|
|
173
|
+
const content = ejs.render(await fs.readFile(routeTemplate, 'utf-8'), { ...config });
|
|
174
|
+
await fs.writeFile(routePath, content);
|
|
175
|
+
await fs.remove(path.join(targetDir, 'src/interfaces/routes', `${routeName}.ejs`));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// --- Render All Use Cases ---
|
|
179
|
+
const useCaseDir = path.join(targetDir, 'src/usecases');
|
|
180
|
+
if (await fs.pathExists(useCaseDir)) {
|
|
181
|
+
const useCaseFiles = await fs.readdir(useCaseDir);
|
|
182
|
+
for (const file of useCaseFiles) {
|
|
183
|
+
if (file.endsWith('.ejs')) {
|
|
184
|
+
const templatePathObj = path.join(useCaseDir, file);
|
|
185
|
+
const destPathObj = path.join(useCaseDir, file.replace('.ejs', ''));
|
|
186
|
+
|
|
187
|
+
const template = await fs.readFile(templatePathObj, 'utf-8');
|
|
188
|
+
const content = ejs.render(template, { ...config });
|
|
189
|
+
await fs.writeFile(destPathObj, content);
|
|
190
|
+
await fs.remove(templatePathObj);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
154
194
|
}
|
|
195
|
+
|
|
155
196
|
// Render Server (Clean Arch JS only)
|
|
156
197
|
if (architecture === 'Clean Architecture' && language === 'JavaScript') {
|
|
157
198
|
const serverName = 'server.js';
|
|
@@ -270,7 +311,7 @@ export const renderDynamicComponents = async (templatePath, targetDir, config) =
|
|
|
270
311
|
if (stat.isDirectory()) {
|
|
271
312
|
await renderEjsDir(srcPath, destPath);
|
|
272
313
|
} else if (item.endsWith('.ejs')) {
|
|
273
|
-
const content = ejs.render(await fs.readFile(srcPath, 'utf-8'), {
|
|
314
|
+
const content = ejs.render(await fs.readFile(srcPath, 'utf-8'), { ...config });
|
|
274
315
|
// Remove .ejs extension
|
|
275
316
|
const finalDestPath = destPath.slice(0, -4);
|
|
276
317
|
await fs.writeFile(finalDestPath, content);
|
|
@@ -310,10 +351,11 @@ export const renderSwaggerConfig = async (templatesDir, targetDir, config) => {
|
|
|
310
351
|
if (communication === 'REST APIs' || communication === 'Kafka') {
|
|
311
352
|
const swaggerYmlTemplateSource = path.join(templatesDir, 'common', 'swagger.yml.ejs');
|
|
312
353
|
if (await fs.pathExists(swaggerYmlTemplateSource)) {
|
|
313
|
-
const ymlContent = ejs.render(await fs.readFile(swaggerYmlTemplateSource, 'utf-8'), {
|
|
354
|
+
const ymlContent = ejs.render(await fs.readFile(swaggerYmlTemplateSource, 'utf-8'), { ...config });
|
|
314
355
|
await fs.writeFile(path.join(configDir, 'swagger.yml'), ymlContent);
|
|
315
356
|
}
|
|
316
357
|
|
|
358
|
+
|
|
317
359
|
if (await fs.pathExists(swaggerTsTemplate)) {
|
|
318
360
|
const content = ejs.render(await fs.readFile(swaggerTsTemplate, 'utf-8'), { communication });
|
|
319
361
|
await fs.writeFile(path.join(targetDir, 'src', 'config', 'swagger.ts'), content);
|
|
@@ -347,9 +389,35 @@ export const renderSwaggerConfig = async (templatesDir, targetDir, config) => {
|
|
|
347
389
|
export const setupViews = async (templatesDir, targetDir, config) => {
|
|
348
390
|
const { architecture, viewEngine } = config;
|
|
349
391
|
if (architecture === 'MVC' && viewEngine && viewEngine !== 'None') {
|
|
350
|
-
const
|
|
392
|
+
const engine = viewEngine.toLowerCase();
|
|
393
|
+
const viewsSource = path.join(templatesDir, 'common', 'views', engine);
|
|
394
|
+
const destDir = path.join(targetDir, 'src/views');
|
|
395
|
+
|
|
351
396
|
if (await fs.pathExists(viewsSource)) {
|
|
352
|
-
await fs.
|
|
397
|
+
await fs.ensureDir(destDir);
|
|
398
|
+
const items = await fs.readdir(viewsSource);
|
|
399
|
+
for (const item of items) {
|
|
400
|
+
// Skip auth views here; they are handled by auth-setup.js
|
|
401
|
+
if (item.startsWith('login.') || item.startsWith('signup.')) continue;
|
|
402
|
+
|
|
403
|
+
const srcPath = path.join(viewsSource, item);
|
|
404
|
+
const isTemplate = item.endsWith('.ejs');
|
|
405
|
+
|
|
406
|
+
// Determine target name: remove .ejs only if it's a double extension (e.g., .ejs.ejs or .pug.ejs)
|
|
407
|
+
// Otherwise keep the original name (e.g., index.ejs should remain index.ejs)
|
|
408
|
+
let targetName = item;
|
|
409
|
+
if (item.endsWith('.ejs.ejs') || item.endsWith('.pug.ejs')) {
|
|
410
|
+
targetName = item.slice(0, -4);
|
|
411
|
+
}
|
|
412
|
+
const destPath = path.join(destDir, targetName);
|
|
413
|
+
|
|
414
|
+
if (isTemplate) {
|
|
415
|
+
const content = ejs.render(await fs.readFile(srcPath, 'utf-8'), config);
|
|
416
|
+
await fs.writeFile(destPath, content);
|
|
417
|
+
} else {
|
|
418
|
+
await fs.copy(srcPath, destPath);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
353
421
|
}
|
|
354
422
|
}
|
|
355
423
|
};
|
|
@@ -392,7 +460,9 @@ export const processAllTests = async (targetDir, config) => {
|
|
|
392
460
|
const template = await fs.readFile(itemPath, 'utf-8');
|
|
393
461
|
const content = ejs.render(template, config);
|
|
394
462
|
|
|
395
|
-
|
|
463
|
+
if (content.trim()) {
|
|
464
|
+
await fs.writeFile(targetTestPath, content);
|
|
465
|
+
}
|
|
396
466
|
await fs.remove(itemPath);
|
|
397
467
|
}
|
|
398
468
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import ejs from 'ejs';
|
|
4
|
+
|
|
5
|
+
export const setupAuth = async (templatesDir, targetDir, config) => {
|
|
6
|
+
const { auth, architecture, language, communication, viewEngine } = config;
|
|
7
|
+
if (!auth || auth.includes('None') || !auth.includes('JWT')) return;
|
|
8
|
+
|
|
9
|
+
const langExt = language === 'TypeScript' ? 'ts' : 'js';
|
|
10
|
+
const authSource = path.join(templatesDir, 'common', 'auth', langExt);
|
|
11
|
+
const viewsSource = path.join(templatesDir, 'common', 'views');
|
|
12
|
+
|
|
13
|
+
// 1. JWT Service
|
|
14
|
+
await renderAuthComponent(authSource, targetDir, 'services', `jwtService.${langExt}`, config);
|
|
15
|
+
|
|
16
|
+
// 2. Auth Middleware
|
|
17
|
+
await renderAuthComponent(authSource, targetDir, 'middleware', `authMiddleware.${langExt}`, config);
|
|
18
|
+
|
|
19
|
+
// 3. Auth Controller (Login only)
|
|
20
|
+
await renderAuthComponent(authSource, targetDir, 'controllers', `authController.${langExt}`, config);
|
|
21
|
+
|
|
22
|
+
// 4. Auth Routes
|
|
23
|
+
await renderAuthComponent(authSource, targetDir, 'routes', `authRoutes.${langExt}`, config);
|
|
24
|
+
|
|
25
|
+
// 5. MVC Views (if applicable)
|
|
26
|
+
if (architecture === 'MVC' && viewEngine && viewEngine !== 'None') {
|
|
27
|
+
const engine = viewEngine.toLowerCase();
|
|
28
|
+
const views = ['login', 'signup'];
|
|
29
|
+
for (const view of views) {
|
|
30
|
+
const viewTemplate = path.join(viewsSource, engine, `${view}.${engine}.ejs`);
|
|
31
|
+
if (await fs.pathExists(viewTemplate)) {
|
|
32
|
+
const content = ejs.render(await fs.readFile(viewTemplate, 'utf-8'), config);
|
|
33
|
+
const destPath = path.join(targetDir, 'src', 'views', `${view}.${engine}`);
|
|
34
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
35
|
+
await fs.writeFile(destPath, content);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 6. Restructuring for Clean Architecture
|
|
41
|
+
if (architecture === 'Clean Architecture') {
|
|
42
|
+
await restructureAuthForCleanArch(targetDir, langExt);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async function renderAuthComponent(authSource, targetDir, subDir, fileName, config) {
|
|
47
|
+
const templatePath = path.join(authSource, subDir, `${fileName}.ejs`);
|
|
48
|
+
if (await fs.pathExists(templatePath)) {
|
|
49
|
+
const content = ejs.render(await fs.readFile(templatePath, 'utf-8'), config);
|
|
50
|
+
const destPath = path.join(targetDir, 'src', subDir, fileName);
|
|
51
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
52
|
+
await fs.writeFile(destPath, content);
|
|
53
|
+
|
|
54
|
+
// Render Spec
|
|
55
|
+
const specName = fileName.replace(`.${config.language === 'TypeScript' ? 'ts' : 'js'}`, `.spec.${config.language === 'TypeScript' ? 'ts' : 'js'}`);
|
|
56
|
+
const specTemplate = path.join(authSource, subDir, `${specName}.ejs`);
|
|
57
|
+
if (await fs.pathExists(specTemplate)) {
|
|
58
|
+
const specContent = ejs.render(await fs.readFile(specTemplate, 'utf-8'), config);
|
|
59
|
+
const testDir = path.join(targetDir, 'tests', 'unit', subDir);
|
|
60
|
+
await fs.ensureDir(testDir);
|
|
61
|
+
await fs.writeFile(path.join(testDir, specName), specContent);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function restructureAuthForCleanArch(targetDir, ext) {
|
|
67
|
+
// Services -> Infrastructure/Auth
|
|
68
|
+
await fs.ensureDir(path.join(targetDir, 'src/infrastructure/auth'));
|
|
69
|
+
await fs.ensureDir(path.join(targetDir, 'tests/unit/infrastructure/auth'));
|
|
70
|
+
if (await fs.pathExists(path.join(targetDir, `src/services/jwtService.${ext}`))) {
|
|
71
|
+
await fs.move(
|
|
72
|
+
path.join(targetDir, `src/services/jwtService.${ext}`),
|
|
73
|
+
path.join(targetDir, `src/infrastructure/auth/jwtService.${ext}`),
|
|
74
|
+
{ overwrite: true }
|
|
75
|
+
);
|
|
76
|
+
if (await fs.pathExists(path.join(targetDir, `tests/unit/services/jwtService.spec.${ext}`))) {
|
|
77
|
+
await fs.move(
|
|
78
|
+
path.join(targetDir, `tests/unit/services/jwtService.spec.${ext}`),
|
|
79
|
+
path.join(targetDir, `tests/unit/infrastructure/auth/jwtService.spec.${ext}`),
|
|
80
|
+
{ overwrite: true }
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Controllers -> Interfaces/Controllers/auth
|
|
86
|
+
await fs.ensureDir(path.join(targetDir, 'src/interfaces/controllers/auth'));
|
|
87
|
+
await fs.ensureDir(path.join(targetDir, 'tests/unit/interfaces/controllers/auth'));
|
|
88
|
+
if (await fs.pathExists(path.join(targetDir, `src/controllers/authController.${ext}`))) {
|
|
89
|
+
await fs.move(
|
|
90
|
+
path.join(targetDir, `src/controllers/authController.${ext}`),
|
|
91
|
+
path.join(targetDir, `src/interfaces/controllers/auth/authController.${ext}`),
|
|
92
|
+
{ overwrite: true }
|
|
93
|
+
);
|
|
94
|
+
if (await fs.pathExists(path.join(targetDir, `tests/unit/controllers/authController.spec.${ext}`))) {
|
|
95
|
+
await fs.move(
|
|
96
|
+
path.join(targetDir, `tests/unit/controllers/authController.spec.${ext}`),
|
|
97
|
+
path.join(targetDir, `tests/unit/interfaces/controllers/auth/authController.spec.${ext}`),
|
|
98
|
+
{ overwrite: true }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Middleware -> Infrastructure/Webserver/Middleware
|
|
104
|
+
await fs.ensureDir(path.join(targetDir, 'src/infrastructure/webserver/middleware'));
|
|
105
|
+
await fs.ensureDir(path.join(targetDir, 'tests/unit/infrastructure/webserver/middleware'));
|
|
106
|
+
if (await fs.pathExists(path.join(targetDir, `src/middleware/authMiddleware.${ext}`))) {
|
|
107
|
+
await fs.move(
|
|
108
|
+
path.join(targetDir, `src/middleware/authMiddleware.${ext}`),
|
|
109
|
+
path.join(targetDir, `src/infrastructure/webserver/middleware/authMiddleware.${ext}`),
|
|
110
|
+
{ overwrite: true }
|
|
111
|
+
);
|
|
112
|
+
if (await fs.pathExists(path.join(targetDir, `tests/unit/middleware/authMiddleware.spec.${ext}`))) {
|
|
113
|
+
await fs.move(
|
|
114
|
+
path.join(targetDir, `tests/unit/middleware/authMiddleware.spec.${ext}`),
|
|
115
|
+
path.join(targetDir, `tests/unit/infrastructure/webserver/middleware/authMiddleware.spec.${ext}`),
|
|
116
|
+
{ overwrite: true }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Routes -> Interfaces/Routes (No unit tests for routes normally, but move if exists)
|
|
122
|
+
await fs.ensureDir(path.join(targetDir, 'src/interfaces/routes'));
|
|
123
|
+
if (await fs.pathExists(path.join(targetDir, `src/routes/authRoutes.${ext}`))) {
|
|
124
|
+
await fs.move(
|
|
125
|
+
path.join(targetDir, `src/routes/authRoutes.${ext}`),
|
|
126
|
+
path.join(targetDir, `src/interfaces/routes/authRoutes.${ext}`),
|
|
127
|
+
{ overwrite: true }
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Cleanup empty dirs in src and tests/unit
|
|
132
|
+
const dirsToCleanup = [
|
|
133
|
+
'src/services', 'src/controllers', 'src/middleware', 'src/routes',
|
|
134
|
+
'tests/unit/services', 'tests/unit/controllers', 'tests/unit/middleware'
|
|
135
|
+
];
|
|
136
|
+
for (const dir of dirsToCleanup) {
|
|
137
|
+
const fullDir = path.join(targetDir, dir);
|
|
138
|
+
if (await fs.pathExists(fullDir) && (await fs.readdir(fullDir)).length === 0) {
|
|
139
|
+
await fs.remove(fullDir);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
@@ -30,12 +30,14 @@ export const setupCaching = async (templatesDir, targetDir, config) => {
|
|
|
30
30
|
const useCases = language === 'TypeScript'
|
|
31
31
|
? [
|
|
32
32
|
{ name: 'getAllUsers.ts', src: 'getAllUsers.ts.ejs' },
|
|
33
|
+
{ name: 'getUserById.ts', src: 'getUserById.ts.ejs' },
|
|
33
34
|
{ name: 'createUser.ts', src: 'createUser.ts.ejs' },
|
|
34
35
|
{ name: 'updateUser.ts', src: 'updateUser.ts.ejs' },
|
|
35
36
|
{ name: 'deleteUser.ts', src: 'deleteUser.ts.ejs' }
|
|
36
37
|
]
|
|
37
38
|
: [
|
|
38
39
|
{ name: 'GetAllUsers.js', src: 'GetAllUsers.js.ejs' },
|
|
40
|
+
{ name: 'GetUserById.js', src: 'GetUserById.js.ejs' },
|
|
39
41
|
{ name: 'CreateUser.js', src: 'CreateUser.js.ejs' },
|
|
40
42
|
{ name: 'UpdateUser.js', src: 'UpdateUser.js.ejs' },
|
|
41
43
|
{ name: 'DeleteUser.js', src: 'DeleteUser.js.ejs' }
|
|
@@ -48,8 +50,13 @@ export const setupCaching = async (templatesDir, targetDir, config) => {
|
|
|
48
50
|
const useCaseSource = path.join(templatesDir, 'common', 'caching', 'clean', langExt, uc.src);
|
|
49
51
|
if (await fs.pathExists(useCaseSource)) {
|
|
50
52
|
const ucContent = await fs.readFile(useCaseSource, 'utf-8');
|
|
51
|
-
const renderedUc = ejs.render(ucContent, {
|
|
53
|
+
const renderedUc = ejs.render(ucContent, { ...config });
|
|
52
54
|
await fs.writeFile(path.join(useCaseTargetDir, uc.name), renderedUc);
|
|
55
|
+
// Cleanup the template in target dir
|
|
56
|
+
const templateInTarget = path.join(useCaseTargetDir, uc.src);
|
|
57
|
+
if (await fs.pathExists(templateInTarget)) {
|
|
58
|
+
await fs.remove(templateInTarget);
|
|
59
|
+
}
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
}
|
|
@@ -19,7 +19,8 @@ export const setupDatabase = async (templatesDir, targetDir, config) => {
|
|
|
19
19
|
// Create initial migration file with timestamp
|
|
20
20
|
const timestamp = new Date().toISOString().replace(/[-T:.Z]/g, '').slice(0, 14); // YYYYMMDDHHMMSS
|
|
21
21
|
const migrationTemplate = await fs.readFile(path.join(templatesDir, 'common', 'migrations', 'init.js.ejs'), 'utf-8');
|
|
22
|
-
|
|
22
|
+
const migrationContent = ejs.render(migrationTemplate, { ...config });
|
|
23
|
+
await fs.writeFile(path.join(targetDir, 'migrations', `${timestamp}-initial-setup.js`), migrationContent);
|
|
23
24
|
|
|
24
25
|
} else if (database !== 'None') {
|
|
25
26
|
// Flyway for SQL
|
|
@@ -28,5 +28,6 @@ export const copyCommonFiles = async (templatesDir, targetDir, language) => {
|
|
|
28
28
|
|
|
29
29
|
if (language === 'TypeScript') {
|
|
30
30
|
await fs.copy(path.join(templatesDir, 'common', 'tsconfig.json'), path.join(targetDir, 'tsconfig.json'));
|
|
31
|
+
await fs.copy(path.join(templatesDir, 'common', 'tsconfig.eslint.json'), path.join(targetDir, 'tsconfig.eslint.json'));
|
|
31
32
|
}
|
|
32
33
|
};
|
package/lib/prompts.js
CHANGED
|
@@ -85,6 +85,25 @@ export const getProjectDetails = async (options = {}) => {
|
|
|
85
85
|
choices: ['No', 'Yes'],
|
|
86
86
|
default: "No",
|
|
87
87
|
when: (answers) => options.includeSecurity === undefined && (options.ciProvider || answers.ciProvider) !== 'None'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'select',
|
|
91
|
+
name: 'advancedOptions',
|
|
92
|
+
message: 'Enable Advanced Options (Authentication, etc.)?',
|
|
93
|
+
choices: ['No', 'Yes'],
|
|
94
|
+
default: 'No',
|
|
95
|
+
when: options.advancedOptions === undefined
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'select',
|
|
99
|
+
name: 'auth',
|
|
100
|
+
message: 'Select Authentication Mode:',
|
|
101
|
+
choices: ['None', 'JWT'],
|
|
102
|
+
default: 'None',
|
|
103
|
+
when: (answers) => {
|
|
104
|
+
const advanced = options.advancedOptions !== undefined ? options.advancedOptions : answers.advancedOptions;
|
|
105
|
+
return (advanced === 'Yes' || advanced === true) && !options.auth;
|
|
106
|
+
}
|
|
88
107
|
}
|
|
89
108
|
];
|
|
90
109
|
|
|
@@ -96,5 +115,25 @@ export const getProjectDetails = async (options = {}) => {
|
|
|
96
115
|
result.includeSecurity = result.includeSecurity === 'Yes';
|
|
97
116
|
}
|
|
98
117
|
|
|
118
|
+
if (typeof result.advancedOptions === 'string') {
|
|
119
|
+
result.advancedOptions = result.advancedOptions === 'Yes';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Normalize auth to array if it's a string from the list prompt
|
|
123
|
+
if (typeof result.auth === 'string') {
|
|
124
|
+
result.auth = [result.auth];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Default auth if not provided
|
|
128
|
+
if (!result.auth) {
|
|
129
|
+
result.auth = ['None'];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Filter out 'None' if 'JWT' is selected
|
|
133
|
+
if (result.auth.includes('JWT')) {
|
|
134
|
+
result.auth = result.auth.filter(a => a !== 'None');
|
|
135
|
+
}
|
|
136
|
+
|
|
99
137
|
return result;
|
|
100
138
|
};
|
|
139
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-quickstart-structure",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The ultimate nodejs quickstart structure CLI to scaffold Node.js microservices with MVC or Clean Architecture",
|
|
6
6
|
"main": "bin/index.js",
|
|
@@ -63,11 +63,12 @@
|
|
|
63
63
|
"inquirer": "^13.3.2"
|
|
64
64
|
},
|
|
65
65
|
"overrides": {
|
|
66
|
-
"esbuild": "^0.25.0"
|
|
66
|
+
"esbuild": "^0.25.0",
|
|
67
|
+
"vite": "^6.4.2"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"snyk": "^1.1303.2",
|
|
70
|
-
"vitepress": "^1.
|
|
71
|
+
"vitepress": "^1.6.4"
|
|
71
72
|
},
|
|
72
73
|
"files": [
|
|
73
74
|
"bin",
|
|
@@ -76,4 +77,4 @@
|
|
|
76
77
|
"README.md",
|
|
77
78
|
"CHANGELOG.md"
|
|
78
79
|
]
|
|
79
|
-
}
|
|
80
|
+
}
|