@saulpaulus17/node-module-generator 2.0.4 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  2. package/.github/ISSUE_TEMPLATE/custom.md +10 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. package/.github/workflows/release.yml +13 -5
  5. package/CONTRIBUTING.md +110 -0
  6. package/DOCS_STEPS.md +40 -0
  7. package/README.md +131 -70
  8. package/assets/banner.png +0 -0
  9. package/bin/cli.js +0 -0
  10. package/generator/dto.generator.js +4 -4
  11. package/generator/module.generator.js +32 -11
  12. package/generator/repository.generator.js +3 -3
  13. package/generator/resource.generator.js +12 -15
  14. package/generator/usecase.generator.js +2 -2
  15. package/package.json +10 -2
  16. package/src/modules/Auth/Auth.module.js +15 -0
  17. package/src/modules/Auth/application/dtos/auth.dto.js +10 -0
  18. package/src/modules/Auth/application/usecases/AuthUseCase.js +12 -0
  19. package/src/modules/Auth/application/usecases/AuthUseCase.test.js +30 -0
  20. package/src/modules/Auth/domain/entities/Auth.js +5 -0
  21. package/src/modules/Auth/domain/repositories/AuthRepository.js +9 -0
  22. package/src/modules/Auth/infrastructure/repositories/PrismaAuthRepository.js +15 -0
  23. package/src/modules/Auth/interfaces/controllers/AuthController.js +15 -0
  24. package/src/modules/Auth/interfaces/controllers/AuthController.test.js +49 -0
  25. package/src/modules/Auth/interfaces/routes/auth.routes.js +9 -0
  26. package/src/modules/Auth/package.json +3 -0
  27. package/templates/module/controller.ejs +6 -7
  28. package/templates/module/controller.test.ejs +12 -9
  29. package/templates/module/di.ejs +9 -10
  30. package/templates/module/dto.ejs +10 -7
  31. package/templates/module/entity.ejs +2 -5
  32. package/templates/module/repository.impl.ejs +9 -17
  33. package/templates/module/repository.interface.ejs +3 -5
  34. package/templates/module/route.ejs +7 -7
  35. package/templates/module/usecase.ejs +7 -10
  36. package/templates/module/usecase.test.ejs +13 -9
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Screenshots**
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ **Desktop (please complete the following information):**
27
+ - OS: [e.g. iOS]
28
+ - Browser [e.g. chrome, safari]
29
+ - Version [e.g. 22]
30
+
31
+ **Smartphone (please complete the following information):**
32
+ - Device: [e.g. iPhone6]
33
+ - OS: [e.g. iOS8.1]
34
+ - Browser [e.g. stock browser, safari]
35
+ - Version [e.g. 22]
36
+
37
+ **Additional context**
38
+ Add any other context about the problem here.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: Custom issue template
3
+ about: Describe this issue template's purpose here.
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -5,10 +5,18 @@ on:
5
5
  tags:
6
6
  - 'v*.*.*'
7
7
 
8
+ # Prevent multiple simultaneous releases
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
8
13
  jobs:
9
14
  publish:
10
15
  runs-on: ubuntu-latest
11
-
16
+ permissions:
17
+ contents: read
18
+ id-token: write # Required for NPM provenance
19
+
12
20
  steps:
13
21
  - name: Checkout
14
22
  uses: actions/checkout@v4
@@ -19,13 +27,13 @@ jobs:
19
27
  node-version: 20
20
28
  registry-url: 'https://registry.npmjs.org'
21
29
 
22
- - name: Install deps
23
- run: npm install
30
+ - name: Install dependencies
31
+ run: npm ci
24
32
 
25
- - name: Run test
33
+ - name: Run tests
26
34
  run: npm test
27
35
 
28
36
  - name: Publish to npm
29
- run: npm publish --access public
37
+ run: npm publish --access public --provenance
30
38
  env:
31
39
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,110 @@
1
+ # Contributing to Node Module Generator
2
+
3
+ First off, thank you for considering contributing to **Node Module Generator (NMG)**! It is people like you who make this tool a better resource for the entire Node.js community.
4
+
5
+ Please take a moment to review this document to understand the contribution process.
6
+
7
+ ---
8
+
9
+ ## ⚖️ Code of Conduct
10
+
11
+ By participating in this project, you agree to abide by our Code of Conduct. We expect all contributors to maintain a respectful, inclusive, and professional environment.
12
+
13
+ ---
14
+
15
+ ## 🛠️ Getting Started
16
+
17
+ ### Prerequisites
18
+ - **Node.js**: v18.0.0 or higher
19
+ - **NPM**, **Yarn**, or **PNPM**
20
+
21
+ ### Development Setup
22
+ 1. Fork the repository on GitHub.
23
+ 2. Clone your fork locally:
24
+ ```bash
25
+ git clone https://github.com/YOUR_USERNAME/node-module-generator.git
26
+ cd node-module-generator
27
+ ```
28
+ 3. Install dependencies:
29
+ ```bash
30
+ npm install
31
+ ```
32
+
33
+ ### 🧪 Local Testing
34
+ To test your changes locally, you can run the CLI directly from the source:
35
+ ```bash
36
+ node bin/cli.js <command> <name>
37
+ ```
38
+
39
+ Alternatively, link it globally for local development:
40
+ ```bash
41
+ npm link
42
+ # Now you can use 'nmg' command locally
43
+ ```
44
+
45
+ ---
46
+
47
+ ## 📈 How to Contribute
48
+
49
+ ### Reporting Bugs
50
+ - Check the **Issues** tab to see if the bug has already been reported.
51
+ - If not, open a new issue. Include a clear title, a description of the bug, steps to reproduce, and the expected vs. actual behavior.
52
+
53
+ ### Proposing Features
54
+ - Open an issue titled `feat: <Description>` to discuss the proposal before starting any implementation.
55
+
56
+ ### Pull Request Process
57
+ 1. Create a new branch for your feature or fix:
58
+ ```bash
59
+ git checkout -b feat/your-feature-name
60
+ # OR
61
+ git checkout -b fix/your-bug-name
62
+ ```
63
+ 2. Implement your changes. Ensure you adhere to **Clean Architecture** patterns.
64
+ 3. **Write Tests**: If you add a new feature or fix a bug, please include a test case.
65
+ 4. Run the full test suite:
66
+ ```bash
67
+ npm test
68
+ ```
69
+ 5. Commit your changes using **Conventional Commits** (see below).
70
+ 6. Push to your fork and submit a Pull Request to the `main` branch.
71
+
72
+ ---
73
+
74
+ ## 📝 Coding Standards
75
+
76
+ - **Clean Architecture**: Maintain strict separation between Domain, Application, Infrastructure, and Interface layers.
77
+ - **ESM/CJS**: New modules should follow the latest ESM templates.
78
+ - **DRY & SOLID**: Write clean, modular, and reusable code.
79
+
80
+ ---
81
+
82
+ ## 🏷️ Commit Message Guidelines
83
+
84
+ We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification for our commit messages:
85
+
86
+ - `feat:`: A new feature for the user.
87
+ - `fix:`: A bug fix for the user.
88
+ - `docs:`: Documentation only changes.
89
+ - `refactor:`: A code change that neither fixes a bug nor adds a feature.
90
+ - `test:`: Adding missing tests or correcting existing tests.
91
+ - `chore:`: Changes to the build process or auxiliary tools.
92
+
93
+ Example:
94
+ ```text
95
+ feat(generator): add support for Zod schemas
96
+ ```
97
+
98
+ ---
99
+
100
+ ## ✅ Pull Request Checklist
101
+
102
+ Before submitting your PR, please ensure:
103
+ 1. [ ] The code follows the project's architecture and style.
104
+ 2. [ ] All tests pass locally (`npm test`).
105
+ 3. [ ] All status checks on GitHub (CI) pass.
106
+ 4. [ ] Documentation is updated if necessary.
107
+ 5. [ ] Commit messages follow the Conventional Commits standard.
108
+
109
+ ---
110
+ Thank you for your contribution! 🚀
package/DOCS_STEPS.md ADDED
@@ -0,0 +1,40 @@
1
+ # Langkah-langkah Setelah Generate Modul Baru
2
+
3
+ Setelah menjalankan perintah `nmg module <name>`, ikuti langkah-langkah berikut untuk mengintegrasikan modul ke dalam proyek Node.js Anda:
4
+
5
+ ## 1. Registrasi di Awilix Container (`src/container.js`)
6
+
7
+ Buka file `src/container.js` dan tambahkan registrasi untuk repository jika Anda ingin menggunakan alias spesifik:
8
+
9
+ ```javascript
10
+ import { asFunction } from 'awilix';
11
+
12
+ // ... di dalam container.register({ ... })
13
+ container.register({
14
+ // Contoh alias: [module]Repository -> prisma[Module]Repository
15
+ authRepository: asFunction(({ prismaAuthRepository }) => prismaAuthRepository).scoped(),
16
+ });
17
+ ```
18
+
19
+ *Catatan: Secara default, generator Awilix biasanya mengauto-load file. Pastikan loader Anda dikonfigurasi untuk memuat folder `usecases`, `repositories`, `controllers`, dan `routes`.*
20
+
21
+ ## 2. Registrasi Route di Express (`src/app.js`)
22
+
23
+ Buka file `src/app.js` dan daftarkan router dari modul yang baru dibuat:
24
+
25
+ ```javascript
26
+ // ...
27
+ app.use('/api/v1/auth', container.resolve('authRoutes'));
28
+ // ...
29
+ ```
30
+
31
+ ## 3. Implementasi Detail Bisnis
32
+ * **Domain**: Tentukan skema data di `domain/entities`.
33
+ * **Repository Interface**: Tambahkan method yang dibutuhkan di `domain/repositories`.
34
+ * **Repository Implementation**: Implementasikan query database (Prisma) di `infrastructure/repositories`.
35
+ * **UseCase**: Tulis logika bisnis utama di `application/usecases`.
36
+ * **Controller**: Tangani input request dan panggil UseCase di `interfaces/controllers`.
37
+ * **Routes**: Definisikan endpoint HTTP di `interfaces/routes`.
38
+
39
+ ---
40
+ *Generated by node-module-generator*
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  <div align="center">
2
- <h1>🚀 Node Module Generator</h1>
3
- <p>A robust CLI tool for scaffolding Express.js projects using Clean Architecture & Dependency Injection.</p>
2
+ <img src="./assets/banner.png" alt="Node Module Generator Banner" width="600" style="max-width: 100%;" />
3
+ <h1>🚀 Node Module Generator (NMG)</h1>
4
+ <p><strong>The ultimate CLI companion for rapid, enterprise-grade Node.js scaffolding.</strong></p>
4
5
  <p>
5
6
  <a href="https://github.com/saul-paulus/node-module-generator/actions/workflows/ci.yml">
6
7
  <img src="https://github.com/saul-paulus/node-module-generator/actions/workflows/ci.yml/badge.svg" alt="CI Status">
7
8
  </a>
8
9
  <a href="https://www.npmjs.com/package/@saulpaulus17/node-module-generator">
9
- <img src="https://img.shields.io/npm/v/@saulpaulus17/node-module-generator.svg" alt="NPM Version" />
10
+ <img src="https://img.shields.io/npm/v/@saulpaulus17/node-module-generator.svg?logo=npm&logoColor=white" alt="NPM Version" />
10
11
  </a>
11
12
  <a href="https://www.npmjs.com/package/@saulpaulus17/node-module-generator">
12
- <img src="https://img.shields.io/npm/dt/@saulpaulus17/node-module-generator.svg" alt="NPM Downloads" />
13
+ <img src="https://img.shields.io/npm/dt/@saulpaulus17/node-module-generator.svg?logo=npm&logoColor=white" alt="NPM Downloads" />
13
14
  </a>
14
15
  <a href="https://github.com/saul-paulus/node-module-generator/blob/main/LICENSE">
15
16
  <img src="https://img.shields.io/npm/l/@saulpaulus17/node-module-generator.svg" alt="License" />
@@ -19,106 +20,166 @@
19
20
 
20
21
  ---
21
22
 
22
- ## 📖 Overview
23
+ ## 🏛️ Architecture Overview
24
+
25
+ Node Module Generator (NMG) enforces **Clean Architecture** principles to ensure your backend remains scalable, testable, and decoupled. It is purpose-built for high-performance **Express.js** environments using **Awilix** for Dependency Injection.
26
+
27
+ ### Layered Structure
28
+ ```mermaid
29
+ graph TD
30
+ UI[Interfaces Layer: Controllers & Routes] --> APP[Application Layer: Use Cases & DTOs]
31
+ APP --> DOM[Domain Layer: Entities & Repository Interfaces]
32
+ INF[Infrastructure Layer: Repository Impl & External Services] --> DOM
33
+
34
+ subgraph "Inner Layers"
35
+ DOM
36
+ APP
37
+ end
38
+
39
+ subgraph "Outer Layers"
40
+ UI
41
+ INF
42
+ end
43
+ ```
23
44
 
24
- **Node Module Generator** provides a world-class Developer Experience (DX) equivalent to the robust CLIs found in ecosystems like NestJS or Angular, but designed specifically for pure Node.js environments utilizing **Express.js** and **Clean Architecture**.
45
+ ---
25
46
 
26
- It instantly scaffolds fully-tested, decoupled, and highly cohesive module structures with built-in Dependency Injection using **Awilix**.
47
+ ## 🔥 Key Features
27
48
 
28
- ## Features
49
+ - 💎 **Clean Architecture by Design**: Strict separation into Domain, Application, Infrastructure, and Interface layers.
50
+ - 💉 **Native Dependency Injection**: Fully pre-configured for **Awilix**, providing seamless DI management.
51
+ - 🧪 **Test-Ready Scaffolding**: Automatically generates **Jest** test suites for Controllers and Use Cases.
52
+ - 🚀 **Modern Tooling**: Native support for **ES Modules (ESM)**, **Prisma ORM**, and **Joi/Zod** DTO patterns.
53
+ - 🤖 **Granular Control**: Generate full modules or individual components (UseCases, Repos, DTOs) without disrupting existing code.
29
54
 
30
- - 🏗️ **Clean Architecture by Default**: Automatically separates concerns into Domain, Application, Infrastructure, and Interface layers.
31
- - 💉 **Dependency Injection Ready**: Auto-generates Awilix configurations mapped correctly across use cases, controllers, and repositories.
32
- - 🧪 **Test-Driven Design**: Scaffolds adjoining `*.test.js` files containing boilerplates for Jest to promote TDD.
33
- - 🤖 **Continuous Integration**: Codebase natively incorporates GitHub Actions for automated unit testing checks.
34
- - 🧩 **Granular Scaffolding**: Generate specific components (UseCases, Repositories, DTOs) dynamically on demand without overriding existing folders!
55
+ ---
35
56
 
36
57
  ## 📦 Installation
37
58
 
38
59
  ### Prerequisites
39
- - **Node.js**: v18.0.0 or higher.
40
- - **NPM** or **Yarn**.
60
+ - **Node.js**: v18.0.0 or higher (LTS recommended)
61
+ - **NPM**, **Yarn**, or **PNPM**
41
62
 
42
- To use this CLI tool locally or globally on your machine, you can install it directly from NPM.
63
+ ### Global Installation (Recommended)
64
+ Install NMG globally to access the command from any project.
43
65
 
44
66
  ```bash
45
- # Install globally via NPM
46
67
  npm install -g @saulpaulus17/node-module-generator
47
68
  ```
48
69
 
49
- ## 🚀 Usage
70
+ ### Direct Execution
71
+ Run it on-the-fly without a permanent installation:
72
+
73
+ ```bash
74
+ npx @saulpaulus17/node-module-generator <command> <name>
75
+ ```
76
+
77
+ ---
78
+
79
+ ## 🛠️ Target Project Dependencies
80
+
81
+ To ensure the modules generated by NMG function correctly, your main project must have the following core dependencies installed:
82
+
83
+ ### Production Dependencies
84
+ ```bash
85
+ npm install express awilix @prisma/client
86
+ ```
87
+
88
+ ### Development Dependencies
89
+ ```bash
90
+ npm install --save-dev jest
91
+ ```
92
+
93
+ > [!TIP]
94
+ > These dependencies are essential because the generated code relies on Express for routing, Awilix for Dependency Injection, and Prisma for the data layer.
95
+
96
+ ---
50
97
 
51
- Once installed globally, you can execute the CLI commands from any of your Node.js project directories using the `nmg` command keyword.
98
+ ## 🚀 Detailed Usage
52
99
 
53
- ### Granular CLI Commands
54
- Scaffold specifically what you need, exactly how you do it in NestJS:
100
+ ### 1. Generating a Full Module
101
+ Scaffolds a complete standard architecture with all 4 layers and initial unit tests.
55
102
 
56
103
  ```bash
57
- # 1. Scaffolds a new module architecture and empty DI registry
58
- nmg module product
104
+ nmg module Auth
105
+ ```
59
106
 
60
- # 2. Creates a specific Use Case and its Test inside an existing module
61
- nmg usecase updateProduct --module=product
107
+ ### 2. Generating Individual Components
108
+ Quickly add specific components to an existing module structure.
62
109
 
63
- # 3. Creates Domain Entity and Repository Interfaces/Implementations
64
- nmg repository product
110
+ ```bash
111
+ # Add a new UseCase (e.g., login) to the Auth module
112
+ nmg usecase login --module=Auth
65
113
 
66
- # 4. Scaffolds a DTO validation schema
67
- nmg dto getProduct --module=product
114
+ # Add a Repository Interface and Implementation (Prisma)
115
+ nmg repository User
68
116
 
69
- # 5. Generates an entire full-stack CRUD Resource (Controller, Entity, Repos, DI, etc.)
70
- nmg resource order
117
+ # Add a DTO validation schema
118
+ nmg dto userRegistration --module=Auth
71
119
  ```
72
120
 
73
- _(Note: If you haven't installed the package globally, you can run it via `npx @saulpaulus17/node-module-generator <command> <name>`)_
121
+ ---
74
122
 
75
- ## 📂 Full Resource Structure
123
+ ## 📂 Project Blueprint
76
124
 
77
- Running the powerhouse command `nmg resource user` instantly generates the following decoupled blueprint within your project's `src/modules` directory:
125
+ Scaffolding a module (e.g., `nmg module Product`) produces the following industry-standard structure:
78
126
 
79
127
  ```text
80
- src/modules/user/
81
- ├── application/ # Orchestration & Use Cases
82
- │ ├── dtos/ # (Generated schema references)
83
- │ └── usecases/
84
- │ ├── create-user.usecase.js # Business logically perfectly isolated
85
- │ └── create-user.usecase.test.js # Scaffolded test harness
86
- ├── domain/ # Blueprints & Business Rules (Pure Logic)
87
- │ ├── entities/
88
- │ │ └── user.entity.js
89
- │ └── repositories/ # Repository Interfaces (Contracts)
90
- │ └── user.repository.interface.js
91
- ├── infrastructure/ # Technical Details & Implementation
92
- │ ├── repositories/ # Implementations (mocked to replace with Prisma/TypeORM)
93
- │ │ └── user.repository.impl.js
94
- │ └── validation/ # Input validation schemas
95
- └── create-user.schema.js # Auto-stubbed validation schema
96
- ├── interfaces/ # Entry Points (Web/API)
97
- ├── controllers/
98
- │ ├── user.controller.js # Express.js class handlers
99
- └── user.controller.test.js
100
- └── routes/
101
- │ └── user.routes.js # Express routers integrating the controller
102
- └── user.module.js # Centralised Awilix Dependency Injection bindings
128
+ src/modules/Product/
129
+ ├── application/
130
+ │ ├── dtos/ # DTO schemas (e.g., product.dto.js)
131
+ │ └── usecases/ # Business orchestration
132
+ │ ├── ProductUseCase.js # Logic implementation
133
+ │ └── ProductUseCase.test.js # Unit tests
134
+ ├── domain/
135
+ │ ├── entities/ # Business entity definitions
136
+ │ │ └── Product.js
137
+ │ └── repositories/ # Repository Interface (Contracts)
138
+ │ └── ProductRepository.js
139
+ ├── infrastructure/
140
+ │ ├── repositories/ # Implementation (default: Prisma)
141
+ │ │ └── PrismaProductRepository.js
142
+ ├── interfaces/
143
+ ├── controllers/ # Express handlers
144
+ │ │ ├── ProductController.js
145
+ │ └── ProductController.test.js
146
+ └── routes/ # Express routes & method binding
147
+ └── product.routes.js
148
+ └── Product.module.js # Central Awilix Module Registration
103
149
  ```
104
150
 
105
- ## 🛠️ Technological Footprint
151
+ ---
152
+
153
+ ## 🛠️ Post-Scaffolding Integration
106
154
 
107
- The scaffolded code integrates perfectly if you are using the following libraries in your Express application:
155
+ To finalize your new module integration, follow these standard steps:
108
156
 
109
- - **[Express.js](https://expressjs.com/)** - Web Framework
110
- - **[Awilix](https://github.com/jeffijoe/awilix)** - Powerful Dependency Injection container
111
- - **[Jest](https://jestjs.io/)** - For the colocated testing environments
112
- - **[Joi](https://joi.dev/)** - For infrastructure schema validations
157
+ 1. **DI Registration**: Open `src/container.js` and register any specific repository aliases or scoped usecases.
158
+ 2. **Route Mounting**: Mount the generated router in `src/app.js`:
159
+ ```javascript
160
+ app.use('/api/v1/product', container.resolve('productRoutes'));
161
+ ```
162
+ 3. **Detailed Implementation**: Build out the specific logic in the generated templates (which are already integrated via Awilix).
113
163
 
114
- ## 🤝 Contributing
164
+ ---
165
+
166
+ ## 🤝 Contributing & Support
115
167
 
116
- Contributions are always welcome! Feel free to open issues or submit Pull Requests.
117
- 1. Fork the repo and create your branch (`git checkout -b feature/amazing-feature`).
118
- 2. Commit your changes (`git commit -m 'feat: add amazing feature'`).
119
- 3. Push to the branch (`git push origin feature/amazing-feature`).
120
- 4. Open a Pull Request.
168
+ We welcome contributions from the community!
169
+ 1. Fork the project.
170
+ 2. Create your Feature Branch (`git checkout -b feat/NewFeature`).
171
+ 3. Commit your changes (`git commit -m 'feat: Add some NewFeature'`).
172
+ 4. Push to the Branch (`git push origin feat/NewFeature`).
173
+ 5. Open a Pull Request.
174
+
175
+ ---
121
176
 
122
177
  ## 📝 License
123
178
 
124
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
179
+ Distributed under the **MIT License**. See `LICENSE` for more information.
180
+
181
+ ---
182
+ <div align="center">
183
+ <p>Built with ❤️ for modern Node.js developers</p>
184
+ <sub>Copyright © 2024 saul-paulus</sub>
185
+ </div>
Binary file
package/bin/cli.js CHANGED
File without changes
@@ -5,8 +5,8 @@ const { pascalCase, camelCase } = require("../utils/case.util");
5
5
 
6
6
  module.exports = async function (schemaName, moduleName) {
7
7
  const basePath = path.join(process.cwd(), "src/modules", moduleName);
8
- const validationDir = path.join(basePath, "infrastructure/validation");
9
- fs.ensureDirSync(validationDir);
8
+ const dtoDir = path.join(basePath, "application/dtos");
9
+ fs.ensureDirSync(dtoDir);
10
10
 
11
11
  const templateData = {
12
12
  name: moduleName,
@@ -15,7 +15,7 @@ module.exports = async function (schemaName, moduleName) {
15
15
  };
16
16
 
17
17
  const templateContent = await ejs.renderFile(path.join(__dirname, "../templates/module/dto.ejs"), templateData);
18
- fs.writeFileSync(path.join(validationDir, `${schemaName}.schema.js`), templateContent);
18
+ fs.writeFileSync(path.join(dtoDir, `${schemaName.toLowerCase()}.dto.js`), templateContent);
19
19
 
20
- console.log(`✔ DTO Schema ${schemaName} generated inside module ${moduleName}.`);
20
+ console.log(`✔ DTO ${schemaName} generated inside module ${moduleName} at application/dtos.`);
21
21
  };
@@ -9,13 +9,9 @@ module.exports = async function (name) {
9
9
  const dirs = [
10
10
  "domain/entities",
11
11
  "domain/repositories",
12
- "domain/services",
13
12
  "application/usecases",
14
13
  "application/dtos",
15
14
  "infrastructure/repositories",
16
- "infrastructure/validation",
17
- "infrastructure/security",
18
- "infrastructure/services",
19
15
  "interfaces/controllers",
20
16
  "interfaces/routes",
21
17
  ];
@@ -23,12 +19,37 @@ module.exports = async function (name) {
23
19
  // Create directories
24
20
  dirs.forEach((dir) => fs.ensureDirSync(path.join(basePath, dir)));
25
21
 
26
- console.log(`✔ Module ${name} directory structure created.`);
22
+
23
+ const templateData = {
24
+ name,
25
+ className: pascalCase(name),
26
+ camelName: camelCase(name),
27
+ useCaseClassName: `${pascalCase(name)}UseCase`,
28
+ useCaseFileName: `${name.toLowerCase()}.usecase`,
29
+ };
30
+
31
+ const renderAndWrite = async (templateName, outputPath) => {
32
+ const templateContent = await ejs.renderFile(
33
+ path.join(__dirname, "../templates/module", templateName),
34
+ templateData
35
+ );
36
+ fs.writeFileSync(path.join(basePath, outputPath), templateContent);
37
+ };
38
+
39
+ await renderAndWrite("controller.ejs", `interfaces/controllers/${pascalCase(name)}Controller.js`);
40
+ await renderAndWrite("controller.test.ejs", `interfaces/controllers/${pascalCase(name)}Controller.test.js`);
41
+ await renderAndWrite("route.ejs", `interfaces/routes/${name.toLowerCase()}.routes.js`);
42
+
43
+ await renderAndWrite("usecase.ejs", `application/usecases/${pascalCase(name)}UseCase.js`);
44
+ await renderAndWrite("usecase.test.ejs", `application/usecases/${pascalCase(name)}UseCase.test.js`);
27
45
 
28
- // Optionally create an empty module DI file
29
- const diFile = path.join(basePath, `${name}.module.js`);
30
- if (!fs.existsSync(diFile)) {
31
- fs.writeFileSync(diFile, `module.exports = function register${pascalCase(name)}Module(container) {\n container.register({\n // Inject dependencies here\n });\n};\n`);
32
- console.log(`✔ Module DI registry ${name}.module.js created.`);
33
- }
46
+ await renderAndWrite("entity.ejs", `domain/entities/${pascalCase(name)}.js`);
47
+ await renderAndWrite("repository.interface.ejs", `domain/repositories/${pascalCase(name)}Repository.js`);
48
+
49
+ await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/Prisma${pascalCase(name)}Repository.js`);
50
+ await renderAndWrite("dto.ejs", `application/dtos/${name.toLowerCase()}.dto.js`);
51
+
52
+ await renderAndWrite("di.ejs", `${name}.module.js`);
53
+
54
+ console.log(`✔ Module ${name} directory structure, standard files, and tests created.`);
34
55
  };
@@ -28,9 +28,9 @@ module.exports = async function (moduleName) {
28
28
  fs.writeFileSync(path.join(basePath, outputPath), templateContent);
29
29
  };
30
30
 
31
- await renderAndWrite("entity.ejs", `domain/entities/${moduleName}.entity.js`);
32
- await renderAndWrite("repository.interface.ejs", `domain/repositories/${moduleName}.repository.interface.js`);
33
- await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/${moduleName}.repository.impl.js`);
31
+ await renderAndWrite("entity.ejs", `domain/entities/${pascalCase(moduleName)}.js`);
32
+ await renderAndWrite("repository.interface.ejs", `domain/repositories/${pascalCase(moduleName)}Repository.js`);
33
+ await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/Prisma${pascalCase(moduleName)}Repository.js`);
34
34
 
35
35
  console.log(`✔ Repository patterns for ${moduleName} generated successfully.`);
36
36
  };
@@ -9,25 +9,22 @@ module.exports = async function (name) {
9
9
  const dirs = [
10
10
  "domain/entities",
11
11
  "domain/repositories",
12
- "domain/services",
13
12
  "application/usecases",
14
13
  "application/dtos",
15
14
  "infrastructure/repositories",
16
- "infrastructure/validation",
17
- "infrastructure/security",
18
- "infrastructure/services",
19
15
  "interfaces/controllers",
20
16
  "interfaces/routes",
21
17
  ];
22
18
 
23
19
  dirs.forEach((dir) => fs.ensureDirSync(path.join(basePath, dir)));
24
20
 
21
+
25
22
  const templateData = {
26
23
  name,
27
24
  className: pascalCase(name),
28
25
  camelName: camelCase(name),
29
- useCaseClassName: `Create${pascalCase(name)}UseCase`,
30
- useCaseFileName: `create-${name}`,
26
+ useCaseClassName: `${pascalCase(name)}UseCase`,
27
+ useCaseFileName: `${name.toLowerCase()}.usecase`,
31
28
  };
32
29
 
33
30
  const renderAndWrite = async (templateName, outputPath) => {
@@ -38,18 +35,18 @@ module.exports = async function (name) {
38
35
  fs.writeFileSync(path.join(basePath, outputPath), templateContent);
39
36
  };
40
37
 
41
- await renderAndWrite("controller.ejs", `interfaces/controllers/${name}.controller.js`);
42
- await renderAndWrite("controller.test.ejs", `interfaces/controllers/${name}.controller.test.js`);
43
- await renderAndWrite("route.ejs", `interfaces/routes/${name}.routes.js`);
38
+ await renderAndWrite("controller.ejs", `interfaces/controllers/${pascalCase(name)}Controller.js`);
39
+ await renderAndWrite("controller.test.ejs", `interfaces/controllers/${pascalCase(name)}Controller.test.js`);
40
+ await renderAndWrite("route.ejs", `interfaces/routes/${name.toLowerCase()}.routes.js`);
44
41
 
45
- await renderAndWrite("usecase.ejs", `application/usecases/create-${name}.usecase.js`);
46
- await renderAndWrite("usecase.test.ejs", `application/usecases/create-${name}.usecase.test.js`);
42
+ await renderAndWrite("usecase.ejs", `application/usecases/${pascalCase(name)}UseCase.js`);
43
+ await renderAndWrite("usecase.test.ejs", `application/usecases/${pascalCase(name)}UseCase.test.js`);
47
44
 
48
- await renderAndWrite("entity.ejs", `domain/entities/${name}.entity.js`);
49
- await renderAndWrite("repository.interface.ejs", `domain/repositories/${name}.repository.interface.js`);
45
+ await renderAndWrite("entity.ejs", `domain/entities/${pascalCase(name)}.js`);
46
+ await renderAndWrite("repository.interface.ejs", `domain/repositories/${pascalCase(name)}Repository.js`);
50
47
 
51
- await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/${name}.repository.impl.js`);
52
- await renderAndWrite("dto.ejs", `infrastructure/validation/create-${name}.schema.js`);
48
+ await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/Prisma${pascalCase(name)}Repository.js`);
49
+ await renderAndWrite("dto.ejs", `application/dtos/${name.toLowerCase()}.dto.js`);
53
50
 
54
51
  await renderAndWrite("di.ejs", `${name}.module.js`);
55
52
 
@@ -17,10 +17,10 @@ module.exports = async function (useCaseName, moduleName) {
17
17
  };
18
18
 
19
19
  const templateContent = await ejs.renderFile(path.join(__dirname, "../templates/module/usecase.ejs"), templateData);
20
- fs.writeFileSync(path.join(ucDir, `${useCaseName}.usecase.js`), templateContent);
20
+ fs.writeFileSync(path.join(ucDir, `${pascalCase(useCaseName)}UseCase.js`), templateContent);
21
21
 
22
22
  const testContent = await ejs.renderFile(path.join(__dirname, "../templates/module/usecase.test.ejs"), templateData);
23
- fs.writeFileSync(path.join(ucDir, `${useCaseName}.usecase.test.js`), testContent);
23
+ fs.writeFileSync(path.join(ucDir, `${pascalCase(useCaseName)}UseCase.test.js`), testContent);
24
24
 
25
25
  console.log(`✔ Usecase ${useCaseName} generated inside module ${moduleName}.`);
26
26
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saulpaulus17/node-module-generator",
3
- "version": "2.0.4",
3
+ "version": "2.0.8",
4
4
  "description": "CLI tool to grenerate modular scaffolding for nodejs projects following clean arsitecture principles. ",
5
5
  "keywords": [
6
6
  "nodejs",
@@ -11,12 +11,20 @@
11
11
  "license": "MIT",
12
12
  "author": "saul-paulus (ixspx)",
13
13
  "type": "commonjs",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/saul-paulus/node-module-generator.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/saul-paulus/node-module-generator/issues"
20
+ },
21
+ "homepage": "https://github.com/saul-paulus/node-module-generator#readme",
14
22
  "main": "index.js",
15
23
  "bin": {
16
24
  "nmg": "./bin/cli.js"
17
25
  },
18
26
  "scripts": {
19
- "test": "jest"
27
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
20
28
  },
21
29
  "dependencies": {
22
30
  "chalk": "^5.6.2",
@@ -0,0 +1,15 @@
1
+ import { asClass, asFunction } from 'awilix';
2
+
3
+ import AuthController from './interfaces/controllers/AuthController';
4
+ import createAuthRoutes from './interfaces/routes/auth.routes';
5
+ import CreateAuthUseCase from './application/usecases/CreateAuthUseCase';
6
+ import PrismaAuthRepository from './infrastructure/repositories/PrismaAuthRepository';
7
+
8
+ export default function registerAuthModule(container) {
9
+ container.register({
10
+ authController: asClass(AuthController).singleton(),
11
+ authRoutes: asFunction(createAuthRoutes).singleton(),
12
+ CreateAuthUseCase: asClass(CreateAuthUseCase).singleton(),
13
+ authRepository: asFunction(({ prismaAuthRepository }) => prismaAuthRepository).scoped(),
14
+ });
15
+ }
@@ -0,0 +1,10 @@
1
+ // DTO Template
2
+ export default {
3
+ // Define DTO validation schemas here (e.g. Joi, Zod)
4
+ createSchema: {
5
+ // rules
6
+ },
7
+ updateSchema: {
8
+ // rules
9
+ }
10
+ };
@@ -0,0 +1,12 @@
1
+ export default class AuthUseCase {
2
+ constructor({ authRepository, jwtService }) {
3
+ this.authRepository = authRepository;
4
+ this.jwtService = jwtService;
5
+ }
6
+
7
+ async execute(inputData) {
8
+ // Logic for AuthUseCase
9
+ const result = await this.authRepository.create(inputData);
10
+ return { success: true, data: result };
11
+ }
12
+ }
@@ -0,0 +1,30 @@
1
+ import { jest, describe, it, expect, beforeEach } from '@jest/globals';
2
+ import AuthUseCase from './AuthUseCase.js';
3
+
4
+ describe('AuthUseCase', () => {
5
+ let useCase;
6
+ let mockRepository;
7
+
8
+ beforeEach(() => {
9
+ mockRepository = {
10
+ create: jest.fn()
11
+ };
12
+ useCase = new AuthUseCase({
13
+ authRepository: mockRepository,
14
+ jwtService: {}
15
+ });
16
+ });
17
+
18
+ describe('execute', () => {
19
+ it('should call repository create and return success', async () => {
20
+ const mockDto = { name: 'Test' };
21
+ const mockResult = { id: 1, ...mockDto };
22
+ mockRepository.create.mockResolvedValue(mockResult);
23
+
24
+ const result = await useCase.execute(mockDto);
25
+
26
+ expect(mockRepository.create).toHaveBeenCalledWith(mockDto);
27
+ expect(result).toEqual({ success: true, data: mockResult });
28
+ });
29
+ });
30
+ });
@@ -0,0 +1,5 @@
1
+ export default class Auth {
2
+ constructor(props) {
3
+ Object.assign(this, props);
4
+ }
5
+ }
@@ -0,0 +1,9 @@
1
+ export default class AuthRepository {
2
+ async findById(id) {
3
+ throw new Error('Method not implemented.');
4
+ }
5
+
6
+ async create(data) {
7
+ throw new Error('Method not implemented.');
8
+ }
9
+ }
@@ -0,0 +1,15 @@
1
+ export default class PrismaAuthRepository {
2
+ constructor({ prisma }) {
3
+ this.prisma = prisma;
4
+ }
5
+
6
+ async findById(id) {
7
+ return this.prisma.auths.findUnique({
8
+ where: { id },
9
+ });
10
+ }
11
+
12
+ async create(data) {
13
+ return this.prisma.auths.create({ data });
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ export default class AuthController {
2
+ constructor({ authRepository, authUseCase }) {
3
+ this.authRepository = authRepository;
4
+ this.authUseCase = authUseCase;
5
+ }
6
+
7
+ async index(req, res, next) {
8
+ try {
9
+ const result = await this.authUseCase.execute(req.body);
10
+ res.json(result);
11
+ } catch (error) {
12
+ next(error);
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,49 @@
1
+ import { jest, describe, it, expect, beforeEach } from '@jest/globals';
2
+ import AuthController from './AuthController.js';
3
+
4
+ describe('AuthController', () => {
5
+ let controller;
6
+ let mockUseCase;
7
+ let mockReq;
8
+ let mockRes;
9
+ let mockNext;
10
+
11
+ beforeEach(() => {
12
+ mockUseCase = {
13
+ execute: jest.fn()
14
+ };
15
+ controller = new AuthController({
16
+ authRepository: {},
17
+ authUseCase: mockUseCase
18
+ });
19
+ mockReq = {
20
+ body: { test: 'data' }
21
+ };
22
+ mockRes = {
23
+ status: jest.fn().mockReturnThis(),
24
+ json: jest.fn()
25
+ };
26
+ mockNext = jest.fn();
27
+ });
28
+
29
+ describe('index', () => {
30
+ it('should return result if use case succeeds', async () => {
31
+ const mockResult = { success: true, data: mockReq.body };
32
+ mockUseCase.execute.mockResolvedValue(mockResult);
33
+
34
+ await controller.index(mockReq, mockRes, mockNext);
35
+
36
+ expect(mockUseCase.execute).toHaveBeenCalledWith(mockReq.body);
37
+ expect(mockRes.json).toHaveBeenCalledWith(mockResult);
38
+ });
39
+
40
+ it('should call next with error if use case fails', async () => {
41
+ const error = new Error('Test error');
42
+ mockUseCase.execute.mockRejectedValue(error);
43
+
44
+ await controller.index(mockReq, mockRes, mockNext);
45
+
46
+ expect(mockNext).toHaveBeenCalledWith(error);
47
+ });
48
+ });
49
+ });
@@ -0,0 +1,9 @@
1
+ // routes
2
+ export default ({ authController }) => {
3
+ const express = require('express');
4
+ const router = express.Router();
5
+
6
+ router.get('/', authController.index.bind(authController));
7
+
8
+ return router;
9
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -1,16 +1,15 @@
1
- class <%= className %>Controller {
2
- constructor({ <%= camelName %>UseCase }) {
1
+ export default class <%= className %>Controller {
2
+ constructor({ <%= camelName %>Repository, <%= camelName %>UseCase }) {
3
+ this.<%= camelName %>Repository = <%= camelName %>Repository;
3
4
  this.<%= camelName %>UseCase = <%= camelName %>UseCase;
4
5
  }
5
6
 
6
- create = async (req, res, next) => {
7
+ async index(req, res, next) {
7
8
  try {
8
9
  const result = await this.<%= camelName %>UseCase.execute(req.body);
9
- res.status(201).json({ success: true, data: result });
10
+ res.json(result);
10
11
  } catch (error) {
11
12
  next(error);
12
13
  }
13
- };
14
+ }
14
15
  }
15
-
16
- module.exports = <%= className %>Controller;
@@ -1,4 +1,5 @@
1
- const <%= className %>Controller = require('./<%= name %>.controller');
1
+ import { jest, describe, it, expect, beforeEach } from '@jest/globals';
2
+ import <%= className %>Controller from './<%= className %>Controller.js';
2
3
 
3
4
  describe('<%= className %>Controller', () => {
4
5
  let controller;
@@ -11,7 +12,10 @@ describe('<%= className %>Controller', () => {
11
12
  mockUseCase = {
12
13
  execute: jest.fn()
13
14
  };
14
- controller = new <%= className %>Controller({ <%= camelName %>UseCase: mockUseCase });
15
+ controller = new <%= className %>Controller({
16
+ <%= camelName %>Repository: {},
17
+ <%= camelName %>UseCase: mockUseCase
18
+ });
15
19
  mockReq = {
16
20
  body: { test: 'data' }
17
21
  };
@@ -22,23 +26,22 @@ describe('<%= className %>Controller', () => {
22
26
  mockNext = jest.fn();
23
27
  });
24
28
 
25
- describe('create', () => {
26
- it('should return 201 and result if use case succeeds', async () => {
27
- const mockResult = { id: 1, ...mockReq.body };
29
+ describe('index', () => {
30
+ it('should return result if use case succeeds', async () => {
31
+ const mockResult = { success: true, data: mockReq.body };
28
32
  mockUseCase.execute.mockResolvedValue(mockResult);
29
33
 
30
- await controller.create(mockReq, mockRes, mockNext);
34
+ await controller.index(mockReq, mockRes, mockNext);
31
35
 
32
36
  expect(mockUseCase.execute).toHaveBeenCalledWith(mockReq.body);
33
- expect(mockRes.status).toHaveBeenCalledWith(201);
34
- expect(mockRes.json).toHaveBeenCalledWith({ success: true, data: mockResult });
37
+ expect(mockRes.json).toHaveBeenCalledWith(mockResult);
35
38
  });
36
39
 
37
40
  it('should call next with error if use case fails', async () => {
38
41
  const error = new Error('Test error');
39
42
  mockUseCase.execute.mockRejectedValue(error);
40
43
 
41
- await controller.create(mockReq, mockRes, mockNext);
44
+ await controller.index(mockReq, mockRes, mockNext);
42
45
 
43
46
  expect(mockNext).toHaveBeenCalledWith(error);
44
47
  });
@@ -1,16 +1,15 @@
1
- const { asClass, asFunction } = require('awilix');
1
+ import { asClass, asFunction } from 'awilix';
2
2
 
3
- const <%= className %>Controller = require('./interfaces/controllers/<%= name %>.controller');
4
- const create<%= className %>Routes = require('./interfaces/routes/<%= name %>.routes');
5
- const Create<%= className %>UseCase = require('./application/usecases/create-<%= name %>.usecase');
6
- const <%= className %>Repository = require('./infrastructure/repositories/<%= name %>.repository.impl');
3
+ import <%= className %>Controller from './interfaces/controllers/<%= className %>Controller';
4
+ import create<%= className %>Routes from './interfaces/routes/<%= name.toLowerCase() %>.routes';
5
+ import Create<%= className %>UseCase from './application/usecases/Create<%= className %>UseCase';
6
+ import Prisma<%= className %>Repository from './infrastructure/repositories/Prisma<%= className %>Repository';
7
7
 
8
- module.exports = function register<%= className %>Module(container) {
8
+ export default function register<%= className %>Module(container) {
9
9
  container.register({
10
10
  <%= camelName %>Controller: asClass(<%= className %>Controller).singleton(),
11
11
  <%= camelName %>Routes: asFunction(create<%= className %>Routes).singleton(),
12
- <%= camelName %>UseCase: asClass(Create<%= className %>UseCase).singleton(),
13
- // Providing the interface's name as the injection token for the implementation
14
- <%= camelName %>Repository: asClass(<%= className %>Repository).singleton(),
12
+ Create<%= className %>UseCase: asClass(Create<%= className %>UseCase).singleton(),
13
+ <%= camelName %>Repository: asFunction(({ prisma<%= className %>Repository }) => prisma<%= className %>Repository).scoped(),
15
14
  });
16
- };
15
+ }
@@ -1,7 +1,10 @@
1
- const Joi = require('joi'); // Usually you'd use joi or another lib
2
-
3
- const create<%= className %>Schema = Joi.object({
4
- // Define DTO validation rules
5
- });
6
-
7
- module.exports = create<%= className %>Schema;
1
+ // DTO Template
2
+ export default {
3
+ // Define DTO validation schemas here (e.g. Joi, Zod)
4
+ createSchema: {
5
+ // rules
6
+ },
7
+ updateSchema: {
8
+ // rules
9
+ }
10
+ };
@@ -1,8 +1,5 @@
1
- class <%= className %>Entity {
2
- constructor({ id, ...props }) {
3
- this.id = id;
1
+ export default class <%= className %> {
2
+ constructor(props) {
4
3
  Object.assign(this, props);
5
4
  }
6
5
  }
7
-
8
- module.exports = <%= className %>Entity;
@@ -1,23 +1,15 @@
1
- const I<%= className %>Repository = require('../../domain/repositories/<%= name %>.repository.interface');
2
- const <%= className %>Entity = require('../../domain/entities/<%= name %>.entity');
3
-
4
- class <%= className %>RepositoryImpl extends I<%= className %>Repository {
5
- // If using Prisma, you would do: constructor({ prisma }) { super(); this.prisma = prisma; }
6
- constructor() {
7
- super();
8
- this.db = new Map();
1
+ export default class Prisma<%= className %>Repository {
2
+ constructor({ prisma }) {
3
+ this.prisma = prisma;
9
4
  }
10
5
 
11
- async save(dto) {
12
- const id = Date.now().toString(); // Mock ID generation
13
- const entity = new <%= className %>Entity({ id, ...dto });
14
- this.db.set(id, entity);
15
- return entity;
6
+ async findById(id) {
7
+ return this.prisma.<%= camelName %>s.findUnique({
8
+ where: { id },
9
+ });
16
10
  }
17
11
 
18
- async findById(id) {
19
- return this.db.get(id) || null;
12
+ async create(data) {
13
+ return this.prisma.<%= camelName %>s.create({ data });
20
14
  }
21
15
  }
22
-
23
- module.exports = <%= className %>RepositoryImpl;
@@ -1,11 +1,9 @@
1
- class I<%= className %>Repository {
2
- async save(entity) {
1
+ export default class <%= className %>Repository {
2
+ async findById(id) {
3
3
  throw new Error('Method not implemented.');
4
4
  }
5
5
 
6
- async findById(id) {
6
+ async create(data) {
7
7
  throw new Error('Method not implemented.');
8
8
  }
9
9
  }
10
-
11
- module.exports = I<%= className %>Repository;
@@ -1,9 +1,9 @@
1
- const { Router } = require('express');
2
-
3
- module.exports = function({ <%= camelName %>Controller }) {
4
- const router = Router();
5
-
6
- router.post('/', <%= camelName %>Controller.create);
7
-
1
+ // routes
2
+ export default ({ <%= camelName %>Controller }) => {
3
+ const express = require('express');
4
+ const router = express.Router();
5
+
6
+ router.get('/', <%= camelName %>Controller.index.bind(<%= camelName %>Controller));
7
+
8
8
  return router;
9
9
  };
@@ -1,15 +1,12 @@
1
- class <%= useCaseClassName %> {
2
- constructor({ <%= camelName %>Repository }) {
1
+ export default class <%= className %>UseCase {
2
+ constructor({ <%= camelName %>Repository, jwtService }) {
3
3
  this.<%= camelName %>Repository = <%= camelName %>Repository;
4
+ this.jwtService = jwtService;
4
5
  }
5
6
 
6
- async execute(dto) {
7
- // Validate DTO...
8
- // Apply business logic...
9
-
10
- const entity = await this.<%= camelName %>Repository.save(dto);
11
- return entity;
7
+ async execute(inputData) {
8
+ // Logic for <%= className %>UseCase
9
+ const result = await this.<%= camelName %>Repository.create(inputData);
10
+ return { success: true, data: result };
12
11
  }
13
12
  }
14
-
15
- module.exports = <%= useCaseClassName %>;
@@ -1,26 +1,30 @@
1
- const <%= useCaseClassName %> = require('./<%= useCaseFileName %>.usecase');
1
+ import { jest, describe, it, expect, beforeEach } from '@jest/globals';
2
+ import <%= className %>UseCase from './<%= className %>UseCase.js';
2
3
 
3
- describe('<%= useCaseClassName %>', () => {
4
+ describe('<%= className %>UseCase', () => {
4
5
  let useCase;
5
6
  let mockRepository;
6
7
 
7
8
  beforeEach(() => {
8
9
  mockRepository = {
9
- save: jest.fn()
10
+ create: jest.fn()
10
11
  };
11
- useCase = new <%= useCaseClassName %>({ <%= camelName %>Repository: mockRepository });
12
+ useCase = new <%= className %>UseCase({
13
+ <%= camelName %>Repository: mockRepository,
14
+ jwtService: {}
15
+ });
12
16
  });
13
17
 
14
18
  describe('execute', () => {
15
- it('should save data using the repository and return the entity', async () => {
19
+ it('should call repository create and return success', async () => {
16
20
  const mockDto = { name: 'Test' };
17
- const mockEntity = { id: 1, ...mockDto };
18
- mockRepository.save.mockResolvedValue(mockEntity);
21
+ const mockResult = { id: 1, ...mockDto };
22
+ mockRepository.create.mockResolvedValue(mockResult);
19
23
 
20
24
  const result = await useCase.execute(mockDto);
21
25
 
22
- expect(mockRepository.save).toHaveBeenCalledWith(mockDto);
23
- expect(result).toEqual(mockEntity);
26
+ expect(mockRepository.create).toHaveBeenCalledWith(mockDto);
27
+ expect(result).toEqual({ success: true, data: mockResult });
24
28
  });
25
29
  });
26
30
  });