nodejs-quickstart-structure 2.0.1 → 2.1.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 +25 -0
- package/README.md +64 -66
- 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/config-files.js +6 -0
- 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 +38 -1
- 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 +51 -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 +70 -5
- 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 +55 -22
- 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 +12 -3
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +9 -1
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +23 -1
- 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/usecases/UpdateUser.spec.js.ejs +9 -1
- 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 +71 -10
- 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 +43 -9
- 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 +57 -24
- 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 +12 -3
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +10 -1
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +9 -1
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +55 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +10 -1
- 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 +170 -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 +167 -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/babel.config.js.ejs +5 -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/caching/js/memoryCache.spec.js.ejs +2 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +2 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +2 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +2 -0
- 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/js/mongoose.spec.js.ejs +2 -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/database/ts/mongoose.spec.ts.ejs +2 -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 +13 -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 +11 -2
- 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 +3 -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,29 @@
|
|
|
1
1
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
2
|
+
|
|
3
|
+
## [2.1.1] - 2026-04-24
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Template Security Hardening**: Resolved missing buffer bounds check vulnerability in `uuid` (GHSA-w5hq-g745-h8pq) by adding `"uuid": "^14.0.0"` to template `overrides`.
|
|
7
|
+
- **Template Formatting**: Fixed an issue in `package.json.ejs` causing unexpected empty lines between generated dependencies.
|
|
8
|
+
- **Unit Test Stability**: Resolved persistent `SyntaxError` and `TypeError` in Jest unit tests across all architecture permutations by implementing ESM-compatible transformation patterns for `uuid` and standardizing on `jest.clearAllMocks()` with factory mocks.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Documentation Refinement**: Remade `README.md` and removed emojis for a cleaner, more professional, text-only presentation across the VitePress-based site.
|
|
12
|
+
|
|
13
|
+
## [2.1.0] - 2026-04-20
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- **Pluggable JWT Authentication**: Introduced a robust, production-ready authentication system.
|
|
18
|
+
- Supports **Access & Refresh Token Rotation** for enhanced security.
|
|
19
|
+
- Integrated **Token Revocation (Blacklisting)** via Redis or Memory Cache.
|
|
20
|
+
- Secure logout flows and theft detection for refresh tokens.
|
|
21
|
+
- Seamless integration with both REST and GraphQL protocols across all architectures.
|
|
22
|
+
- **VitePress Documentation Upgrade**: Detailed [Authentication Blueprint](https://paudang.github.io/nodejs-quickstart-structure/blueprints/authentication) and cross-feature documentation updates.
|
|
23
|
+
- **Security Hardening**: Resolved moderate vulnerabilities in `vite` and `vitepress` by implementing version overrides and dependency upgrades (VitePress v1.6.4+).
|
|
24
|
+
- **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.
|
|
25
|
+
|
|
26
|
+
---
|
|
2
27
|
|
|
3
28
|
## [2.0.1] - 2026-04-07
|
|
4
29
|
|
package/README.md
CHANGED
|
@@ -1,65 +1,47 @@
|
|
|
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)
|
|
7
8
|
|
|
8
|
-
### 📈 Real-world Adoption
|
|
9
|
-
| **Metric** | **Insight** |
|
|
10
|
-
| :--- | :--- |
|
|
11
|
-
| 🔥 **4,000+** | Downloads on npm |
|
|
12
|
-
| 🚀 **1,200+** | Recent GitHub Clones |
|
|
13
|
-
| 🌍 **Trusted by** | Devs from **Google**, **Viblo**, and global tech teams |
|
|
14
9
|
|
|
15
10
|
---
|
|
16
11
|
|
|
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
|
-
---
|
|
12
|
+
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.
|
|
25
13
|
|
|
26
|
-
##
|
|
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
|
-
---
|
|
14
|
+
## Table of Contents
|
|
37
15
|
|
|
38
|
-
|
|
16
|
+
- [Quick Start](#choose-your-journey)
|
|
17
|
+
- [What's New](#whats-new-in-v21-the-authentication-release)
|
|
18
|
+
- [Key Features](#key-features)
|
|
19
|
+
- [Professional Standards](#professional-standards)
|
|
20
|
+
- [5,280+ Project Combinations](#5280-project-combinations)
|
|
21
|
+
- [Configuration Options](#configuration-options)
|
|
22
|
+
- [Generated Project Structure](#generated-project-structure)
|
|
23
|
+
- [Documentation](#documentation)
|
|
24
|
+
- [Support & Roadmap](#support--roadmap)
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
26
|
+
## Choose Your Journey
|
|
27
|
+
|
|
28
|
+
| **Path A: Next-Gen Web UI** (Recommended) | **Path B: Interactive CLI** |
|
|
29
|
+
| :--- | :--- |
|
|
30
|
+
| <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"> |
|
|
31
|
+
| [Try Visual Configurator →](https://paudang.github.io/nodejs-quickstart-structure/#configurator) | [See CLI Commands ↓](#path-b-interactive-cli) |
|
|
32
|
+
| **Visual Preview**: Real-time folder simulation. | **Fast & Direct**: Quickly scaffold in terminal. |
|
|
33
|
+
| **Zero-Prompt**: Paste a tailored command. | **AI-Ready**: Generates `.cursorrules`. |
|
|
48
34
|
|
|
49
35
|
---
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Generate your professional Node.js project in seconds without installing anything globally:
|
|
37
|
+
### Path B: Interactive CLI
|
|
38
|
+
**Scaffold your project directly from your terminal in seconds.**
|
|
54
39
|
|
|
55
40
|
```bash
|
|
56
41
|
npx nodejs-quickstart-structure@latest init
|
|
57
42
|
```
|
|
58
43
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
If you prefer to install it globally:
|
|
62
|
-
|
|
44
|
+
*Or install globally:*
|
|
63
45
|
```bash
|
|
64
46
|
npm install -g nodejs-quickstart-structure
|
|
65
47
|
# Then run:
|
|
@@ -68,7 +50,21 @@ nodejs-quickstart init
|
|
|
68
50
|
|
|
69
51
|
---
|
|
70
52
|
|
|
71
|
-
##
|
|
53
|
+
## What's New in v2.1 (The Authentication Release)
|
|
54
|
+
|
|
55
|
+
The v2.1.0 release is a major leap forward, turning the generator into a **Community Standard**:
|
|
56
|
+
|
|
57
|
+
- **Pluggable JWT Authentication**: Production-ready access & refresh token patterns with automatic PM2/Environment configuration.
|
|
58
|
+
- **AI-Native Foundation**: Built-in `.cursorrules` optimized for **Cursor** & AI agents—projects are "Born to be Autonomously Coded."
|
|
59
|
+
- **Next-Gen Web UI**: A browser-based visual project simulator with real-time folder previews.
|
|
60
|
+
- **Enterprise Clean Architecture**: High-fidelity structure for professional Microservices (TS/JS).
|
|
61
|
+
- **Hardened Security**: Integrated Snyk & SonarCloud logic in core templates.
|
|
62
|
+
- **Zero-Prompt Workflow**: Generate projects with a single CLI command.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Key Features
|
|
72
68
|
|
|
73
69
|
- **Interactive CLI**: Smooth, guided configuration process.
|
|
74
70
|
- **Multiple Architectures**: Supports both **MVC** and **Clean Architecture**.
|
|
@@ -76,38 +72,39 @@ nodejs-quickstart init
|
|
|
76
72
|
- **Database Ready**: Pre-configured for **MySQL**, **PostgreSQL**, or **MongoDB**.
|
|
77
73
|
- **Communication Patterns**: Supports **REST**, **GraphQL** (Apollo), and **Kafka** (Event-driven).
|
|
78
74
|
- **Multi-layer Caching**: Integrated **Redis** or built-in **Memory Cache**.
|
|
79
|
-
- **
|
|
75
|
+
- **Pluggable Authentication**: Built-in **JWT** support (Refresh/Access tokens).
|
|
76
|
+
- **AI-Native Optimized**: specifically designed for **Cursor** and AI agents, including built-in `.cursorrules` and Agent Skill prompts.
|
|
80
77
|
|
|
81
78
|
---
|
|
82
79
|
|
|
83
|
-
##
|
|
80
|
+
## Professional Standards
|
|
84
81
|
|
|
85
82
|
We don't just generate boilerplate; we generate **production-ready** foundations. Every project includes:
|
|
86
83
|
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
84
|
+
- **Code Quality**: Pre-configured `Eslint` and `Prettier`.
|
|
85
|
+
- **Enterprise Security**: Integrated **Snyk (SCA)**, **SonarCloud (SAST)**, `Helmet`, `HPP`, and Rate-Limiting.
|
|
86
|
+
- **Robust Error Handling**: Centralized global error middleware with custom error classes (`ApiError`, `NotFoundError`, etc.) — consistent across REST & GraphQL.
|
|
87
|
+
- **Testing Excellence**: Integrated `Jest` and `Supertest` with **>80% Unit Test coverage** out of the box.
|
|
88
|
+
- **DevOps & CI/CD**: Optimized **Multi-Stage Dockerfiles**, health checks, infrastructure retry logic, and workflows for **GitHub Actions**, **Jenkins**, **GitLab CI**, **CircleCI**, and **Bitbucket Pipelines**.
|
|
89
|
+
- **Scalable Deployment**: Integrated **PM2 Ecosystem** config for zero-downtime reloads.
|
|
93
90
|
|
|
94
91
|
---
|
|
95
92
|
|
|
96
|
-
##
|
|
93
|
+
## 5,280+ Project Combinations
|
|
97
94
|
|
|
98
95
|
The CLI supports a massive number of configurations to fit your exact needs:
|
|
99
96
|
|
|
100
|
-
- **
|
|
101
|
-
- **MVC Architecture**:
|
|
102
|
-
- **Clean Architecture**:
|
|
103
|
-
- **
|
|
104
|
-
- Every combination can be generated across
|
|
97
|
+
- **480 Core Combinations**:
|
|
98
|
+
- **MVC Architecture**: 360 variants (Languages × View Engines × Databases × Communication Patterns × Caching × Auth)
|
|
99
|
+
- **Clean Architecture**: 120 variants (Languages × Databases × Communication Patterns × Caching × Auth)
|
|
100
|
+
- **5,280+ Total Scenarios**:
|
|
101
|
+
- Every combination can be generated across 5 CI/CD providers.
|
|
105
102
|
- Optional **Enterprise-Grade Security Hardening** doubles the scenarios.
|
|
106
103
|
- Every single scenario is verified to be compatible with our **80% Coverage Threshold** policy.
|
|
107
104
|
|
|
108
105
|
---
|
|
109
106
|
|
|
110
|
-
##
|
|
107
|
+
## Configuration Options
|
|
111
108
|
|
|
112
109
|
The CLI will guide you through:
|
|
113
110
|
1. **Project Name**
|
|
@@ -117,12 +114,13 @@ The CLI will guide you through:
|
|
|
117
114
|
5. **Database**: `MySQL` | `PostgreSQL` | `MongoDB`
|
|
118
115
|
6. **Communication**: `REST` | `GraphQL` | `Kafka`
|
|
119
116
|
7. **Caching**: `None` | `Redis` | `Memory Cache`
|
|
120
|
-
8. **
|
|
121
|
-
9. **
|
|
117
|
+
8. **Auth**: `None` | `JWT`
|
|
118
|
+
9. **CI/CD**: `GitHub Actions` | `Jenkins` | `GitLab CI` | `CircleCI` | `Bitbucket Pipelines`
|
|
119
|
+
10. **Security**: (Optional) Snyk & SonarCloud Hardening
|
|
122
120
|
|
|
123
121
|
---
|
|
124
122
|
|
|
125
|
-
##
|
|
123
|
+
## Generated Project Structure
|
|
126
124
|
Depending on your choices, the structure adapts. Here is a **TypeScript + Clean Architecture** preview:
|
|
127
125
|
|
|
128
126
|
```text
|
|
@@ -144,13 +142,13 @@ Depending on your choices, the structure adapts. Here is a **TypeScript + Clean
|
|
|
144
142
|
|
|
145
143
|
---
|
|
146
144
|
|
|
147
|
-
##
|
|
145
|
+
## Documentation
|
|
148
146
|
|
|
149
|
-
For full guides, architecture deep-dives, and feature references, visit our **[Official Documentation Site](https://paudang.github.io/nodejs-quickstart-structure/)**.
|
|
147
|
+
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
148
|
|
|
151
149
|
---
|
|
152
150
|
|
|
153
|
-
##
|
|
151
|
+
## Support & Roadmap
|
|
154
152
|
|
|
155
153
|
### Support the Project
|
|
156
154
|
If this tool helped you build your project faster, please support us:
|
|
@@ -159,11 +157,11 @@ If this tool helped you build your project faster, please support us:
|
|
|
159
157
|
|
|
160
158
|
### Roadmap
|
|
161
159
|
Track our progress and vote for features on our public board:
|
|
162
|
-
|
|
160
|
+
**[View our Public Roadmap on Trello](https://trello.com/b/TPTo8ylF/nodejs-quickstart-structure-product)**
|
|
163
161
|
|
|
164
162
|
---
|
|
165
163
|
|
|
166
|
-
##
|
|
164
|
+
## Why Star us?
|
|
167
165
|
|
|
168
166
|
We are on a mission to build the best AI-Native Node.js scaffolding experience. Your star is not just a "like"—it's a vote of confidence that helps us:
|
|
169
167
|
|
|
@@ -171,7 +169,7 @@ We are on a mission to build the best AI-Native Node.js scaffolding experience.
|
|
|
171
169
|
2. **AI Model Awareness**: Popular repositories are weighted higher by AI coding assistants (Cursor, Copilot, etc.), making the generated code even better.
|
|
172
170
|
3. **Open Source Sustainability**: It motivates us to keep building and shipping "Enterprise-Grade" features for free.
|
|
173
171
|
|
|
174
|
-
If this tool saved you hours of work, **[please give us a Star!](https://github.com/paudang/nodejs-quickstart-structure)**
|
|
172
|
+
If this tool saved you hours of work, **[please give us a Star!](https://github.com/paudang/nodejs-quickstart-structure)**
|
|
175
173
|
|
|
176
174
|
---
|
|
177
175
|
|
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
|
}
|
|
@@ -53,6 +53,12 @@ export const renderProfessionalConfig = async (templatesDir, targetDir, config)
|
|
|
53
53
|
const jestE2eContent = ejs.render(jestE2eTemplate, { ...config });
|
|
54
54
|
await fs.writeFile(path.join(targetDir, 'jest.e2e.config.js'), jestE2eContent);
|
|
55
55
|
|
|
56
|
+
if (config.language === 'JavaScript') {
|
|
57
|
+
const babelTemplate = await fs.readFile(path.join(templatesDir, 'common', 'babel.config.js.ejs'), 'utf-8');
|
|
58
|
+
const babelContent = ejs.render(babelTemplate, { ...config });
|
|
59
|
+
await fs.writeFile(path.join(targetDir, 'babel.config.js'), babelContent);
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
// 1. Setup Husky pre-commit (Always for Professional Standard)
|
|
57
63
|
const huskyDir = path.join(targetDir, '.husky');
|
|
58
64
|
await fs.ensureDir(huskyDir);
|