@saulpaulus17/node-module-generator 2.0.2 → 2.0.5

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 (34) 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/CONTRIBUTING.md +110 -0
  5. package/DOCS_STEPS.md +40 -0
  6. package/README.md +123 -60
  7. package/bin/cli.js +0 -0
  8. package/generator/dto.generator.js +4 -4
  9. package/generator/module.generator.js +34 -11
  10. package/generator/repository.generator.js +3 -3
  11. package/generator/resource.generator.js +14 -15
  12. package/generator/usecase.generator.js +2 -2
  13. package/package.json +2 -2
  14. package/src/modules/Auth/Auth.module.js +15 -0
  15. package/src/modules/Auth/application/dtos/auth.dto.js +10 -0
  16. package/src/modules/Auth/application/usecases/AuthUseCase.js +12 -0
  17. package/src/modules/Auth/application/usecases/AuthUseCase.test.js +30 -0
  18. package/src/modules/Auth/domain/entities/Auth.js +5 -0
  19. package/src/modules/Auth/domain/repositories/AuthRepository.js +9 -0
  20. package/src/modules/Auth/infrastructure/repositories/PrismaAuthRepository.js +15 -0
  21. package/src/modules/Auth/interfaces/controllers/AuthController.js +15 -0
  22. package/src/modules/Auth/interfaces/controllers/AuthController.test.js +49 -0
  23. package/src/modules/Auth/interfaces/routes/auth.routes.js +9 -0
  24. package/src/modules/Auth/package.json +3 -0
  25. package/templates/module/controller.ejs +6 -7
  26. package/templates/module/controller.test.ejs +12 -9
  27. package/templates/module/di.ejs +9 -10
  28. package/templates/module/dto.ejs +10 -7
  29. package/templates/module/entity.ejs +2 -5
  30. package/templates/module/repository.impl.ejs +9 -17
  31. package/templates/module/repository.interface.ejs +3 -5
  32. package/templates/module/route.ejs +7 -7
  33. package/templates/module/usecase.ejs +7 -10
  34. 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.
@@ -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,103 +1,166 @@
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="https://raw.githubusercontent.com/saul-paulus/node-module-generator/main/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>
9
+ <a href="https://www.npmjs.com/package/@saulpaulus17/node-module-generator">
10
+ <img src="https://img.shields.io/npm/v/@saulpaulus17/node-module-generator.svg?logo=npm&logoColor=white" alt="NPM Version" />
11
+ </a>
12
+ <a href="https://www.npmjs.com/package/@saulpaulus17/node-module-generator">
13
+ <img src="https://img.shields.io/npm/dt/@saulpaulus17/node-module-generator.svg?logo=npm&logoColor=white" alt="NPM Downloads" />
14
+ </a>
15
+ <a href="https://github.com/saul-paulus/node-module-generator/blob/main/LICENSE">
16
+ <img src="https://img.shields.io/npm/l/@saulpaulus17/node-module-generator.svg" alt="License" />
17
+ </a>
8
18
  </p>
9
19
  </div>
10
20
 
11
21
  ---
12
22
 
13
- ## 📖 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
+ ```
14
44
 
15
- **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
+ ---
16
46
 
17
- It instantly scaffolds fully-tested, decoupled, and highly cohesive module structures with built-in Dependency Injection using **Awilix**.
47
+ ## 🔥 Key Features
18
48
 
19
- ## 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.
20
54
 
21
- - 🏗️ **Clean Architecture by Default**: Automatically separates concerns into Domain, Application, Infrastructure, and Interface layers.
22
- - 💉 **Dependency Injection Ready**: Auto-generates Awilix configurations mapped correctly across use cases, controllers, and repositories.
23
- - 🧪 **Test-Driven Design**: Scaffolds adjoining `*.test.js` files containing boilerplates for Jest to promote TDD.
24
- - 🤖 **Continuous Integration**: Codebase natively incorporates GitHub Actions for automated unit testing checks.
25
- - 🧩 **Granular Scaffolding**: Generate specific components (UseCases, Repositories, DTOs) dynamically on demand without overriding existing folders!
55
+ ---
26
56
 
27
57
  ## 📦 Installation
28
58
 
29
- To use this CLI tool locally or globally on your machine, you can install it directly from NPM.
59
+ ### Prerequisites
60
+ - **Node.js**: v18.0.0 or higher (LTS recommended)
61
+ - **NPM**, **Yarn**, or **PNPM**
62
+
63
+ ### Global Installation (Recommended)
64
+ Install NMG globally to access the command from any project.
30
65
 
31
66
  ```bash
32
- # Install globally via NPM
33
67
  npm install -g @saulpaulus17/node-module-generator
34
68
  ```
35
69
 
36
- ## 🚀 Usage
70
+ ### Direct Execution
71
+ Run it on-the-fly without a permanent installation:
37
72
 
38
- Once installed globally, you can execute the CLI commands from any of your Node.js project directories using the `nmg` command keyword.
73
+ ```bash
74
+ npx @saulpaulus17/node-module-generator <command> <name>
75
+ ```
76
+
77
+ ---
39
78
 
40
- ### Granular CLI Commands
41
- Scaffold specifically what you need, exactly how you do it in NestJS:
79
+ ## 🚀 Detailed Usage
80
+
81
+ ### 1. Generating a Full Module
82
+ Scaffolds a complete standard architecture with all 4 layers and initial unit tests.
42
83
 
43
84
  ```bash
44
- # 1. Scaffolds a new module architecture and empty DI registry
45
- nmg module product
85
+ nmg module Auth
86
+ ```
46
87
 
47
- # 2. Creates a specific Use Case and its Test inside an existing module
48
- nmg usecase updateProduct --module=product
88
+ ### 2. Generating Individual Components
89
+ Quickly add specific components to an existing module structure.
49
90
 
50
- # 3. Creates Domain Entity and Repository Interfaces/Implementations
51
- nmg repository product
91
+ ```bash
92
+ # Add a new UseCase (e.g., login) to the Auth module
93
+ nmg usecase login --module=Auth
52
94
 
53
- # 4. Scaffolds a DTO validation schema
54
- nmg dto getProduct --module=product
95
+ # Add a Repository Interface and Implementation (Prisma)
96
+ nmg repository User
55
97
 
56
- # 5. Generates an entire full-stack CRUD Resource (Controller, Entity, Repos, DI, etc.)
57
- nmg resource order
98
+ # Add a DTO validation schema
99
+ nmg dto userRegistration --module=Auth
58
100
  ```
59
101
 
60
- _(Note: If you haven't installed the package globally, you can run it via `npx @saulpaulus17/node-module-generator <command> <name>`)_
102
+ ---
61
103
 
62
- ## 📂 Full Resource Structure
104
+ ## 📂 Project Blueprint
63
105
 
64
- Running the powerhouse command `nmg resource user` instantly generates the following decoupled blueprint within your project's `src/modules` directory:
106
+ Scaffolding a module (e.g., `nmg module Product`) produces the following industry-standard structure:
65
107
 
66
108
  ```text
67
- src/modules/user/
68
- ├── application/ # Orchestration & Use Cases
69
- │ ├── dtos/ # (Generated schema references)
70
- │ └── usecases/
71
- │ ├── create-user.usecase.js # Business logically perfectly isolated
72
- │ └── create-user.usecase.test.js # Scaffolded test harness
73
- ├── domain/ # Blueprints & Business Rules (Pure Logic)
74
- │ ├── entities/
75
- │ │ └── user.entity.js
76
- │ └── repositories/ # Repository Interfaces (Contracts)
77
- │ └── user.repository.interface.js
78
- ├── infrastructure/ # Technical Details & Implementation
79
- │ ├── repositories/ # Implementations (mocked to replace with Prisma/TypeORM)
80
- │ │ └── user.repository.impl.js
81
- │ └── validation/ # Input validation schemas
82
- └── create-user.schema.js # Auto-stubbed validation schema
83
- ├── interfaces/ # Entry Points (Web/API)
84
- ├── controllers/
85
- │ ├── user.controller.js # Express.js class handlers
86
- └── user.controller.test.js
87
- └── routes/
88
- │ └── user.routes.js # Express routers integrating the controller
89
- └── user.module.js # Centralised Awilix Dependency Injection bindings
109
+ src/modules/Product/
110
+ ├── application/
111
+ │ ├── dtos/ # DTO schemas (e.g., product.dto.js)
112
+ │ └── usecases/ # Business orchestration
113
+ │ ├── ProductUseCase.js # Logic implementation
114
+ │ └── ProductUseCase.test.js # Unit tests
115
+ ├── domain/
116
+ │ ├── entities/ # Business entity definitions
117
+ │ │ └── Product.js
118
+ │ └── repositories/ # Repository Interface (Contracts)
119
+ │ └── ProductRepository.js
120
+ ├── infrastructure/
121
+ │ ├── repositories/ # Implementation (default: Prisma)
122
+ │ │ └── PrismaProductRepository.js
123
+ ├── interfaces/
124
+ ├── controllers/ # Express handlers
125
+ │ │ ├── ProductController.js
126
+ │ └── ProductController.test.js
127
+ └── routes/ # Express routes & method binding
128
+ └── product.routes.js
129
+ └── Product.module.js # Central Awilix Module Registration
90
130
  ```
91
131
 
92
- ## 🛠️ Technological Footprint
132
+ ---
133
+
134
+ ## 🛠️ Post-Scaffolding Integration
135
+
136
+ To finalize your new module integration, follow these standard steps:
137
+
138
+ 1. **DI Registration**: Open `src/container.js` and register any specific repository aliases or scoped usecases.
139
+ 2. **Route Mounting**: Mount the generated router in `src/app.js`:
140
+ ```javascript
141
+ app.use('/api/v1/product', container.resolve('productRoutes'));
142
+ ```
143
+ 3. **Detailed Implementation**: Build out the specific logic in the generated templates (which are already integrated via Awilix).
144
+
145
+ ---
93
146
 
94
- The scaffolded code integrates perfectly if you are using the following libraries in your Express application:
147
+ ## 🤝 Contributing & Support
95
148
 
96
- - **[Express.js](https://expressjs.com/)** - Web Framework
97
- - **[Awilix](https://github.com/jeffijoe/awilix)** - Powerful Dependency Injection container
98
- - **[Jest](https://jestjs.io/)** - For the colocated testing environments
99
- - **[Joi](https://joi.dev/)** - For infrastructure schema validations
149
+ We welcome contributions from the community!
150
+ 1. Fork the project.
151
+ 2. Create your Feature Branch (`git checkout -b feat/NewFeature`).
152
+ 3. Commit your changes (`git commit -m 'feat: Add some NewFeature'`).
153
+ 4. Push to the Branch (`git push origin feat/NewFeature`).
154
+ 5. Open a Pull Request.
155
+
156
+ ---
100
157
 
101
158
  ## 📝 License
102
159
 
103
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
160
+ Distributed under the **MIT License**. See `LICENSE` for more information.
161
+
162
+ ---
163
+ <div align="center">
164
+ <p>Built with ❤️ for modern Node.js developers</p>
165
+ <sub>Copyright © 2024 saul-paulus</sub>
166
+ </div>
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,39 @@ 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
+ // Generate scoped package.json for ESM support
23
+ fs.writeJsonSync(path.join(basePath, "package.json"), { type: "module" }, { spaces: 2 });
24
+
25
+ const templateData = {
26
+ name,
27
+ className: pascalCase(name),
28
+ camelName: camelCase(name),
29
+ useCaseClassName: `${pascalCase(name)}UseCase`,
30
+ useCaseFileName: `${name.toLowerCase()}.usecase`,
31
+ };
32
+
33
+ const renderAndWrite = async (templateName, outputPath) => {
34
+ const templateContent = await ejs.renderFile(
35
+ path.join(__dirname, "../templates/module", templateName),
36
+ templateData
37
+ );
38
+ fs.writeFileSync(path.join(basePath, outputPath), templateContent);
39
+ };
40
+
41
+ await renderAndWrite("controller.ejs", `interfaces/controllers/${pascalCase(name)}Controller.js`);
42
+ await renderAndWrite("controller.test.ejs", `interfaces/controllers/${pascalCase(name)}Controller.test.js`);
43
+ await renderAndWrite("route.ejs", `interfaces/routes/${name.toLowerCase()}.routes.js`);
44
+
45
+ await renderAndWrite("usecase.ejs", `application/usecases/${pascalCase(name)}UseCase.js`);
46
+ await renderAndWrite("usecase.test.ejs", `application/usecases/${pascalCase(name)}UseCase.test.js`);
27
47
 
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
- }
48
+ await renderAndWrite("entity.ejs", `domain/entities/${pascalCase(name)}.js`);
49
+ await renderAndWrite("repository.interface.ejs", `domain/repositories/${pascalCase(name)}Repository.js`);
50
+
51
+ await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/Prisma${pascalCase(name)}Repository.js`);
52
+ await renderAndWrite("dto.ejs", `application/dtos/${name.toLowerCase()}.dto.js`);
53
+
54
+ await renderAndWrite("di.ejs", `${name}.module.js`);
55
+
56
+ console.log(`✔ Module ${name} directory structure, standard files, and tests created.`);
34
57
  };
@@ -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,24 @@ 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
+ // Generate scoped package.json for ESM support
22
+ fs.writeJsonSync(path.join(basePath, "package.json"), { type: "module" }, { spaces: 2 });
23
+
25
24
  const templateData = {
26
25
  name,
27
26
  className: pascalCase(name),
28
27
  camelName: camelCase(name),
29
- useCaseClassName: `Create${pascalCase(name)}UseCase`,
30
- useCaseFileName: `create-${name}`,
28
+ useCaseClassName: `${pascalCase(name)}UseCase`,
29
+ useCaseFileName: `${name.toLowerCase()}.usecase`,
31
30
  };
32
31
 
33
32
  const renderAndWrite = async (templateName, outputPath) => {
@@ -38,18 +37,18 @@ module.exports = async function (name) {
38
37
  fs.writeFileSync(path.join(basePath, outputPath), templateContent);
39
38
  };
40
39
 
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`);
40
+ await renderAndWrite("controller.ejs", `interfaces/controllers/${pascalCase(name)}Controller.js`);
41
+ await renderAndWrite("controller.test.ejs", `interfaces/controllers/${pascalCase(name)}Controller.test.js`);
42
+ await renderAndWrite("route.ejs", `interfaces/routes/${name.toLowerCase()}.routes.js`);
44
43
 
45
- await renderAndWrite("usecase.ejs", `application/usecases/create-${name}.usecase.js`);
46
- await renderAndWrite("usecase.test.ejs", `application/usecases/create-${name}.usecase.test.js`);
44
+ await renderAndWrite("usecase.ejs", `application/usecases/${pascalCase(name)}UseCase.js`);
45
+ await renderAndWrite("usecase.test.ejs", `application/usecases/${pascalCase(name)}UseCase.test.js`);
47
46
 
48
- await renderAndWrite("entity.ejs", `domain/entities/${name}.entity.js`);
49
- await renderAndWrite("repository.interface.ejs", `domain/repositories/${name}.repository.interface.js`);
47
+ await renderAndWrite("entity.ejs", `domain/entities/${pascalCase(name)}.js`);
48
+ await renderAndWrite("repository.interface.ejs", `domain/repositories/${pascalCase(name)}Repository.js`);
50
49
 
51
- await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/${name}.repository.impl.js`);
52
- await renderAndWrite("dto.ejs", `infrastructure/validation/create-${name}.schema.js`);
50
+ await renderAndWrite("repository.impl.ejs", `infrastructure/repositories/Prisma${pascalCase(name)}Repository.js`);
51
+ await renderAndWrite("dto.ejs", `application/dtos/${name.toLowerCase()}.dto.js`);
53
52
 
54
53
  await renderAndWrite("di.ejs", `${name}.module.js`);
55
54
 
@@ -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.2",
3
+ "version": "2.0.5",
4
4
  "description": "CLI tool to grenerate modular scaffolding for nodejs projects following clean arsitecture principles. ",
5
5
  "keywords": [
6
6
  "nodejs",
@@ -16,7 +16,7 @@
16
16
  "nmg": "./bin/cli.js"
17
17
  },
18
18
  "scripts": {
19
- "test": "jest"
19
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
20
20
  },
21
21
  "dependencies": {
22
22
  "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
  });