nodejs-quickstart-structure 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -3
- package/README.md +5 -3
- package/lib/generator.js +17 -3
- package/lib/modules/app-setup.js +167 -47
- package/lib/modules/caching-setup.js +13 -0
- package/lib/modules/config-files.js +25 -62
- package/lib/modules/database-setup.js +35 -30
- package/lib/modules/kafka-setup.js +79 -13
- package/package.json +1 -2
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +1 -1
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +21 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +1 -1
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +21 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +2 -3
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +81 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +20 -9
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +8 -4
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +102 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +49 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +1 -1
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +1 -1
- package/templates/clean-architecture/ts/src/index.ts.ejs +15 -11
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +64 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +85 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +2 -3
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +166 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/utils/{error.middleware.ts.ejs → errorMiddleware.ts.ejs} +1 -2
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
- package/templates/common/caching/js/redisClient.js.ejs +4 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +149 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
- package/templates/common/caching/ts/redisClient.ts.ejs +4 -0
- package/templates/common/database/js/database.spec.js.ejs +56 -0
- package/templates/common/database/js/models/User.js.ejs +22 -0
- package/templates/common/database/js/models/User.spec.js.ejs +84 -0
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
- package/templates/common/database/ts/database.spec.ts.ejs +56 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +84 -0
- package/templates/common/database/ts/models/User.ts.ejs +26 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
- package/templates/common/eslint.config.mjs.ejs +11 -2
- package/templates/common/health/js/healthRoute.js.ejs +44 -0
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
- package/templates/common/health/ts/healthRoute.ts.ejs +43 -0
- package/templates/common/jest.config.js.ejs +19 -5
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +21 -0
- package/templates/common/kafka/js/services/kafkaService.js.ejs +13 -4
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +60 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +21 -0
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +61 -0
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +6 -1
- package/templates/common/package.json.ejs +0 -3
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +61 -0
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +160 -0
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +158 -0
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +58 -0
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
- package/templates/common/tsconfig.json +1 -1
- package/templates/mvc/js/src/controllers/userController.js.ejs +4 -31
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +170 -0
- package/templates/mvc/js/src/errors/BadRequestError.js +1 -1
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +21 -0
- package/templates/mvc/js/src/errors/NotFoundError.js +1 -1
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +21 -0
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +47 -0
- package/templates/mvc/js/src/index.js.ejs +11 -9
- package/templates/mvc/js/src/routes/api.spec.js.ejs +36 -0
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +185 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +4 -31
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
- package/templates/mvc/ts/src/errors/BadRequestError.ts +1 -1
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
- package/templates/mvc/ts/src/errors/NotFoundError.ts +1 -1
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
- package/templates/mvc/ts/src/index.ts.ejs +13 -9
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +40 -0
- package/templates/mvc/ts/src/utils/{error.middleware.ts.ejs → errorMiddleware.ts.ejs} +1 -2
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +64 -0
- package/docs/demo.gif +0 -0
- package/docs/generateCase.md +0 -265
- package/docs/generatorFlow.md +0 -233
- package/docs/releaseNoteRule.md +0 -42
- package/docs/ruleDevelop.md +0 -30
- package/templates/common/tests/health.test.ts.ejs +0 -14
- /package/templates/clean-architecture/js/src/infrastructure/webserver/{middlewares/error.middleware.js → middleware/errorMiddleware.js} +0 -0
- /package/templates/mvc/js/src/utils/{error.middleware.js → errorMiddleware.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.14.0] - 2026-03-09
|
|
9
|
+
### Added
|
|
10
|
+
- **Unit test:**
|
|
11
|
+
- Unit Testing Framework: Integrated Jest and ts-jest as the core testing suite.
|
|
12
|
+
|
|
13
|
+
- Scaffolding Logic: Automatically generates .spec.ts files in the tests/ directory mirroring the src/ structure during project initialization.
|
|
14
|
+
|
|
15
|
+
- Quality Gates: Implemented a mandatory Coverage Threshold (>=70%) for lines and functions to ensure long-term maintainability.
|
|
16
|
+
|
|
17
|
+
- Mocking Standards: Included pre-configured mocks for Mongoose (database pings) and Express request/response objects.
|
|
18
|
+
|
|
19
|
+
- NPM Scripts: Added npm test, npm run test:watch, and npm run test:coverage for seamless developer workflow.
|
|
20
|
+
|
|
21
|
+
## [1.13.0] - 2026-03-05
|
|
22
|
+
### Added
|
|
23
|
+
- **Enhanced Health Check System:**
|
|
24
|
+
- Standardized health logic for both MVC and Clean Architecture.
|
|
25
|
+
- Deep verification: Automatically pings the selected database (MySQL, PostgreSQL, MongoDB) during health checks and returns a `DOWN` status with 500 code if the connection is lost.
|
|
26
|
+
- Structured response including `uptime`, `memoryUsage`, `database` connectivity status, and `timestamp`.
|
|
27
|
+
- **Improved Graceful Shutdown:**
|
|
28
|
+
- Standardized signal handling (`SIGINT`, `SIGTERM`) across all architecture types to ensure clean disposal of database and caching resources.
|
|
29
|
+
- Integrated proper shutdown sequences for Redis, MySQL, PostgreSQL, and MongoDB.
|
|
30
|
+
|
|
8
31
|
## [1.12.0] - 2026-03-04
|
|
9
32
|
### Added
|
|
10
33
|
- **Zod Environment Validation:** Replaced manual `dotenv` process calls in server entry points with a centralized schema parser.
|
|
@@ -16,13 +39,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
39
|
|
|
17
40
|
## [1.11.1] - 2026-03-03
|
|
18
41
|
### Fixed
|
|
19
|
-
- Fixed relative import paths in Clean Architecture JS `
|
|
42
|
+
- Fixed relative import paths in Clean Architecture JS `errorMiddleware.js` — changed to correct 3-level relative paths (`../../../`).
|
|
20
43
|
|
|
21
44
|
## [1.11.0] - 2026-03-02
|
|
22
45
|
### Added
|
|
23
46
|
- **Centralized Error Handling Mechanism:** All generated projects now include a standardized, predictable error response structure for both REST APIs and GraphQL communication types.
|
|
24
47
|
- New `src/errors/` directory with custom error classes: `ApiError`, `NotFoundError`, `BadRequestError`.
|
|
25
|
-
- New `error.middleware.{ts|js}` global error handler placed at the end of the Express middleware chain in `src/utils/` (MVC) and `src/utils/` + `src/infrastructure/webserver/
|
|
48
|
+
- New `error.middleware.{ts|js}` global error handler placed at the end of the Express middleware chain in `src/utils/` (MVC) and `src/utils/` + `src/infrastructure/webserver/middleware/` (Clean Architecture).
|
|
26
49
|
- Integrates `winston` logger to automatically log 500-level errors to persistent log files.
|
|
27
50
|
- All controllers updated to pass errors via `next(error)` instead of manually sending responses.
|
|
28
51
|
- **GraphQL:** Apollo Server `formatError` hook configured with `unwrapResolverError` to intercept resolver errors and map `ApiError` instances to structured GraphQL extension codes.
|
|
@@ -32,7 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
32
55
|
### Fixed
|
|
33
56
|
- Fixed `swagger.js` / `swagger.yml` being incorrectly generated for non-REST API configurations (GraphQL, Kafka). Converted static `swagger.js` to `swagger.js.ejs` in MVC JS and Clean Architecture JS templates so `renderSwaggerConfig` can conditionally control its generation.
|
|
34
57
|
- Fixed `userRoutes.ts` in Clean Architecture TypeScript template not passing `NextFunction` to controller methods, causing `TypeScript TS2554: Expected 3 arguments` compile errors during Docker builds.
|
|
35
|
-
- Fixed incorrect relative import paths (`../../../../`) in Clean Architecture JS `
|
|
58
|
+
- Fixed incorrect relative import paths (`../../../../`) in Clean Architecture JS `errorMiddleware.js` — changed to correct 3-level relative paths (`../../../`).
|
|
36
59
|
- Fixed `HTTP_STATUS` being imported without destructuring from `httpCodes.js` (which uses a plain `module.exports = HTTP_STATUS` default export), causing `Cannot read properties of undefined` runtime errors.
|
|
37
60
|
- Fixed `renderErrorMiddleware` in `app-setup.js` deleting from the **template source directory** instead of the generated project's target directory. This caused error middleware templates to disappear from disk after the first test run, breaking all subsequent generations.
|
|
38
61
|
|
package/README.md
CHANGED
|
@@ -31,13 +31,14 @@ We don't just generate boilerplate; we generate **production-ready** foundations
|
|
|
31
31
|
- **🔍 Code Quality**: Pre-configured `Eslint` and `Prettier` for consistent coding standards.
|
|
32
32
|
- **🛡️ Security**: Built-in `Helmet`, `HPP`, `CORS`, and Rate-Limiting middleware.
|
|
33
33
|
- **🚨 Error Handling**: Centralized global error middleware with custom error classes and structured JSON responses. GraphQL uses Apollo's `formatError` hook; REST uses Express error middleware.
|
|
34
|
-
- **🧪 Testing
|
|
34
|
+
- **🧪 Testing Excellence**: Integrated `Jest` and `Supertest`. Every generated project maintains **>70% Unit Test coverage** for controllers, services, and resolvers out of the box.
|
|
35
35
|
- **🔄 CI/CD Integration**: Pre-configured workflows for **GitHub Actions**, **Jenkins**, and **GitLab CI**.
|
|
36
36
|
- **⚓ Git Hooks**: `Husky` and `Lint-Staged` to ensure no bad code is ever committed.
|
|
37
|
+
- **🤝 Reliability**: Advanced Health Checks (`/health`) with deep database pings and Graceful Shutdown workflows for zero-downtime rollouts.
|
|
37
38
|
- **🐳 DevOps**: Highly optimized **Multi-Stage Dockerfile** for small, secure production images.
|
|
38
39
|
- **🚀 Deployment**: Ship confidently with an integrated **PM2 Ecosystem Configuration** for zero-downtime reloads and robust process management.
|
|
39
40
|
|
|
40
|
-
## 🧩
|
|
41
|
+
## 🧩 480+ Project Combinations
|
|
41
42
|
|
|
42
43
|
The CLI supports a massive number of configurations to fit your exact needs:
|
|
43
44
|
|
|
@@ -45,7 +46,8 @@ The CLI supports a massive number of configurations to fit your exact needs:
|
|
|
45
46
|
- **MVC Architecture**: 180 variants (Languages × View Engines × Databases × Communication Patterns × Caching)
|
|
46
47
|
- **Clean Architecture**: 60 variants (Languages × Databases × Communication Patterns × Caching)
|
|
47
48
|
- **480 Total Scenarios**:
|
|
48
|
-
- Every combination can be generated with or without **GitHub Actions CI/CD
|
|
49
|
+
- Every combination can be generated with or without (**GitHub Actions CI/CD** / **Jenkins** or **GitLab CI**), tripling the possibilities.
|
|
50
|
+
- Every single one of these 480 scenarios is verified to be compatible with our 70% Coverage Threshold policy.
|
|
49
51
|
|
|
50
52
|
For a detailed list of all supported cases, check out [docs/generateCase.md](docs/generateCase.md).
|
|
51
53
|
|
package/lib/generator.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import { setupProjectDirectory, copyBaseStructure, copyCommonFiles } from './modules/project-setup.js';
|
|
4
4
|
import { renderPackageJson, renderDockerCompose, renderReadme, renderDockerfile, renderProfessionalConfig, setupCiCd, renderTestSample, renderEnvExample, renderPm2Config } from './modules/config-files.js';
|
|
5
|
-
import { renderIndexFile, renderEnvConfig, renderErrorMiddleware, renderDynamicComponents, renderSwaggerConfig, setupViews as setupSrcViews } from './modules/app-setup.js';
|
|
5
|
+
import { renderIndexFile, renderEnvConfig, renderErrorMiddleware, renderDynamicComponents, renderSwaggerConfig, setupViews as setupSrcViews, processAllTests } from './modules/app-setup.js';
|
|
6
6
|
import { setupDatabase } from './modules/database-setup.js';
|
|
7
7
|
import { setupKafka, setupViews } from './modules/kafka-setup.js';
|
|
8
8
|
import { setupCaching } from './modules/caching-setup.js';
|
|
@@ -11,6 +11,17 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
11
11
|
const __dirname = path.dirname(__filename);
|
|
12
12
|
|
|
13
13
|
export const generateProject = async (config) => {
|
|
14
|
+
// 0. Normalize configuration with defaults
|
|
15
|
+
config = {
|
|
16
|
+
viewEngine: 'None',
|
|
17
|
+
caching: 'None',
|
|
18
|
+
dbName: 'demo',
|
|
19
|
+
ciProvider: 'None',
|
|
20
|
+
communication: 'REST APIs',
|
|
21
|
+
database: 'None',
|
|
22
|
+
...config
|
|
23
|
+
};
|
|
24
|
+
|
|
14
25
|
const { projectName, architecture, language } = config;
|
|
15
26
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
16
27
|
const templatesDir = path.join(__dirname, '../templates');
|
|
@@ -67,8 +78,8 @@ export const generateProject = async (config) => {
|
|
|
67
78
|
await renderSwaggerConfig(templatesDir, targetDir, config);
|
|
68
79
|
|
|
69
80
|
// 13. Professional Config & Tests
|
|
70
|
-
await renderProfessionalConfig(templatesDir, targetDir,
|
|
71
|
-
await renderTestSample(templatesDir, targetDir,
|
|
81
|
+
await renderProfessionalConfig(templatesDir, targetDir, config);
|
|
82
|
+
await renderTestSample(templatesDir, targetDir, config);
|
|
72
83
|
|
|
73
84
|
// 14. CI/CD
|
|
74
85
|
await setupCiCd(templatesDir, targetDir, config);
|
|
@@ -79,6 +90,9 @@ export const generateProject = async (config) => {
|
|
|
79
90
|
// 16. PM2 Configuration
|
|
80
91
|
await renderPm2Config(templatesDir, targetDir, config);
|
|
81
92
|
|
|
93
|
+
// 17. Process All Tests
|
|
94
|
+
await processAllTests(targetDir, config);
|
|
95
|
+
|
|
82
96
|
console.log(`
|
|
83
97
|
====================================================
|
|
84
98
|
Node.js Project Created Successfully!
|
package/lib/modules/app-setup.js
CHANGED
|
@@ -3,20 +3,14 @@ import path from 'path';
|
|
|
3
3
|
import ejs from 'ejs';
|
|
4
4
|
|
|
5
5
|
export const renderIndexFile = async (templatePath, targetDir, config) => {
|
|
6
|
-
const { communication, viewEngine, database, architecture, projectName, language } = config;
|
|
6
|
+
const { communication, viewEngine, database, architecture, projectName, language, caching } = config;
|
|
7
7
|
const indexFileName = language === 'TypeScript' ? 'index.ts' : 'index.js';
|
|
8
8
|
const indexPath = path.join(targetDir, 'src', indexFileName);
|
|
9
9
|
const indexTemplateSource = path.join(templatePath, 'src', `${indexFileName}.ejs`);
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
if (await fs.pathExists(indexTemplateSource)) {
|
|
12
12
|
const indexTemplate = await fs.readFile(indexTemplateSource, 'utf-8');
|
|
13
|
-
const indexContent = ejs.render(indexTemplate, {
|
|
14
|
-
communication,
|
|
15
|
-
viewEngine,
|
|
16
|
-
database,
|
|
17
|
-
architecture,
|
|
18
|
-
projectName
|
|
19
|
-
});
|
|
13
|
+
const indexContent = ejs.render(indexTemplate, { ...config });
|
|
20
14
|
await fs.writeFile(indexPath, indexContent);
|
|
21
15
|
await fs.remove(path.join(targetDir, 'src', `${indexFileName}.ejs`));
|
|
22
16
|
}
|
|
@@ -25,7 +19,7 @@ export const renderIndexFile = async (templatePath, targetDir, config) => {
|
|
|
25
19
|
export const renderEnvConfig = async (templatePath, targetDir, config) => {
|
|
26
20
|
const { language, architecture, database, caching, communication } = config;
|
|
27
21
|
const envExt = language === 'TypeScript' ? 'ts' : 'js';
|
|
28
|
-
|
|
22
|
+
|
|
29
23
|
let configDir = path.join(targetDir, 'src', 'config');
|
|
30
24
|
if (architecture === 'Clean Architecture' && language === 'JavaScript') {
|
|
31
25
|
configDir = path.join(targetDir, 'src', 'infrastructure', 'config');
|
|
@@ -33,14 +27,10 @@ export const renderEnvConfig = async (templatePath, targetDir, config) => {
|
|
|
33
27
|
|
|
34
28
|
const envTemplatePath = path.join(configDir, `env.${envExt}.ejs`);
|
|
35
29
|
const envDestPath = path.join(configDir, `env.${envExt}`);
|
|
36
|
-
|
|
30
|
+
|
|
37
31
|
if (await fs.pathExists(envTemplatePath)) {
|
|
38
32
|
const envTemplate = await fs.readFile(envTemplatePath, 'utf-8');
|
|
39
|
-
const envContent = ejs.render(envTemplate, {
|
|
40
|
-
database,
|
|
41
|
-
caching,
|
|
42
|
-
communication
|
|
43
|
-
});
|
|
33
|
+
const envContent = ejs.render(envTemplate, { ...config });
|
|
44
34
|
await fs.writeFile(envDestPath, envContent);
|
|
45
35
|
await fs.remove(envTemplatePath);
|
|
46
36
|
}
|
|
@@ -48,7 +38,7 @@ export const renderEnvConfig = async (templatePath, targetDir, config) => {
|
|
|
48
38
|
|
|
49
39
|
export const renderErrorMiddleware = async (templatePath, targetDir, config) => {
|
|
50
40
|
const { language, architecture } = config;
|
|
51
|
-
const errName = language === 'TypeScript' ? '
|
|
41
|
+
const errName = language === 'TypeScript' ? 'errorMiddleware.ts' : 'errorMiddleware.js';
|
|
52
42
|
const errTemplateName = `${errName}.ejs`;
|
|
53
43
|
|
|
54
44
|
if (architecture === 'MVC') {
|
|
@@ -68,8 +58,8 @@ export const renderErrorMiddleware = async (templatePath, targetDir, config) =>
|
|
|
68
58
|
await fs.writeFile(utilsDest, await fs.readFile(utilsEjsCopy, 'utf-8'));
|
|
69
59
|
await fs.remove(utilsEjsCopy);
|
|
70
60
|
}
|
|
71
|
-
// Also render the
|
|
72
|
-
const mwDir = path.join(targetDir, 'src/infrastructure/webserver/
|
|
61
|
+
// Also render the middleware version if present (NOT removing from template)
|
|
62
|
+
const mwDir = path.join(targetDir, 'src/infrastructure/webserver/middleware');
|
|
73
63
|
const mwEjsCopy = path.join(mwDir, errTemplateName);
|
|
74
64
|
const mwDest = path.join(mwDir, errName);
|
|
75
65
|
if (await fs.pathExists(mwEjsCopy)) {
|
|
@@ -78,6 +68,16 @@ export const renderErrorMiddleware = async (templatePath, targetDir, config) =>
|
|
|
78
68
|
await fs.remove(mwEjsCopy);
|
|
79
69
|
}
|
|
80
70
|
}
|
|
71
|
+
|
|
72
|
+
// Render errorMiddleware spec template
|
|
73
|
+
const specExt = language === 'TypeScript' ? 'ts' : 'js';
|
|
74
|
+
const specTemplatePath = path.join(templatePath, '../../common/src/utils', `errorMiddleware.spec.${specExt}.ejs`);
|
|
75
|
+
if (await fs.pathExists(specTemplatePath)) {
|
|
76
|
+
const testUtilsDir = path.join(targetDir, 'tests', 'utils');
|
|
77
|
+
await fs.ensureDir(testUtilsDir);
|
|
78
|
+
const specContent = ejs.render(await fs.readFile(specTemplatePath, 'utf-8'), config);
|
|
79
|
+
await fs.writeFile(path.join(testUtilsDir, `errorMiddleware.spec.${specExt}`), specContent);
|
|
80
|
+
}
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
export const renderDynamicComponents = async (templatePath, targetDir, config) => {
|
|
@@ -86,55 +86,147 @@ export const renderDynamicComponents = async (templatePath, targetDir, config) =
|
|
|
86
86
|
// MVC Controller
|
|
87
87
|
if (architecture === 'MVC') {
|
|
88
88
|
const userControllerName = language === 'TypeScript' ? 'userController.ts' : 'userController.js';
|
|
89
|
+
const userControllerSpecName = language === 'TypeScript' ? 'userController.spec.ts' : 'userController.spec.js';
|
|
90
|
+
|
|
89
91
|
const userControllerPath = path.join(targetDir, 'src/controllers', userControllerName);
|
|
92
|
+
const userControllerSpecPath = path.join(targetDir, 'tests/controllers', userControllerSpecName);
|
|
93
|
+
|
|
90
94
|
const userControllerTemplate = path.join(templatePath, 'src/controllers', `${userControllerName}.ejs`);
|
|
95
|
+
const userControllerSpecTemplate = path.join(templatePath, 'src/controllers', `${userControllerSpecName}.ejs`);
|
|
91
96
|
|
|
92
97
|
if (await fs.pathExists(userControllerTemplate)) {
|
|
93
|
-
const content = ejs.render(await fs.readFile(userControllerTemplate, 'utf-8'), {
|
|
98
|
+
const content = ejs.render(await fs.readFile(userControllerTemplate, 'utf-8'), { ...config });
|
|
94
99
|
await fs.writeFile(userControllerPath, content);
|
|
95
100
|
await fs.remove(path.join(targetDir, 'src/controllers', `${userControllerName}.ejs`));
|
|
96
101
|
}
|
|
97
|
-
|
|
102
|
+
|
|
103
|
+
if (await fs.pathExists(userControllerSpecTemplate)) {
|
|
104
|
+
await fs.ensureDir(path.join(targetDir, 'tests/controllers'));
|
|
105
|
+
const content = ejs.render(await fs.readFile(userControllerSpecTemplate, 'utf-8'), { ...config });
|
|
106
|
+
await fs.writeFile(userControllerSpecPath, content);
|
|
107
|
+
await fs.remove(path.join(targetDir, 'src/controllers', `${userControllerSpecName}.ejs`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
98
110
|
// Clean Architecture Repo
|
|
99
111
|
else if (architecture === 'Clean Architecture') {
|
|
100
112
|
const repoName = language === 'TypeScript' ? 'UserRepository.ts' : 'UserRepository.js';
|
|
101
|
-
const
|
|
113
|
+
const repoSpecName = language === 'TypeScript' ? 'UserRepository.spec.ts' : 'UserRepository.spec.js';
|
|
114
|
+
|
|
115
|
+
const repoPath = path.join(targetDir, 'src/infrastructure/repositories', repoName);
|
|
116
|
+
const repoSpecPath = path.join(targetDir, 'tests/infrastructure/repositories', repoSpecName);
|
|
117
|
+
|
|
102
118
|
const repoTemplate = path.join(templatePath, 'src/infrastructure/repositories', `${repoName}.ejs`);
|
|
119
|
+
const repoSpecTemplate = path.join(templatePath, 'src/infrastructure/repositories', `${repoSpecName}.ejs`);
|
|
103
120
|
|
|
104
121
|
if (await fs.pathExists(repoTemplate)) {
|
|
105
|
-
const content = ejs.render(await fs.readFile(repoTemplate, 'utf-8'), {
|
|
122
|
+
const content = ejs.render(await fs.readFile(repoTemplate, 'utf-8'), { ...config });
|
|
106
123
|
await fs.writeFile(repoPath, content);
|
|
107
124
|
await fs.remove(path.join(targetDir, 'src/infrastructure/repositories', `${repoName}.ejs`));
|
|
108
125
|
}
|
|
126
|
+
if (await fs.pathExists(repoSpecTemplate)) {
|
|
127
|
+
await fs.ensureDir(path.join(targetDir, 'tests/infrastructure/repositories'));
|
|
128
|
+
const content = ejs.render(await fs.readFile(repoSpecTemplate, 'utf-8'), { ...config });
|
|
129
|
+
await fs.writeFile(repoSpecPath, content);
|
|
130
|
+
await fs.remove(path.join(targetDir, 'src/infrastructure/repositories', `${repoSpecName}.ejs`));
|
|
131
|
+
}
|
|
109
132
|
|
|
110
133
|
const controllerName = language === 'TypeScript' ? 'userController.ts' : 'userController.js';
|
|
134
|
+
const controllerSpecName = language === 'TypeScript' ? 'userController.spec.ts' : 'userController.spec.js';
|
|
135
|
+
|
|
111
136
|
const controllerPath = path.join(targetDir, 'src/interfaces/controllers', controllerName);
|
|
137
|
+
const controllerSpecPath = path.join(targetDir, 'tests/interfaces/controllers', controllerSpecName);
|
|
138
|
+
|
|
112
139
|
const controllerTemplate = path.join(templatePath, 'src/interfaces/controllers', `${controllerName}.ejs`);
|
|
140
|
+
const controllerSpecTemplate = path.join(templatePath, 'src/interfaces/controllers', `${controllerSpecName}.ejs`);
|
|
113
141
|
|
|
114
142
|
if (await fs.pathExists(controllerTemplate)) {
|
|
115
|
-
const content = ejs.render(await fs.readFile(controllerTemplate, 'utf-8'), {
|
|
143
|
+
const content = ejs.render(await fs.readFile(controllerTemplate, 'utf-8'), { ...config });
|
|
116
144
|
await fs.writeFile(controllerPath, content);
|
|
117
145
|
await fs.remove(path.join(targetDir, 'src/interfaces/controllers', `${controllerName}.ejs`));
|
|
118
146
|
}
|
|
147
|
+
|
|
148
|
+
if (await fs.pathExists(controllerSpecTemplate)) {
|
|
149
|
+
await fs.ensureDir(path.join(targetDir, 'tests/interfaces/controllers'));
|
|
150
|
+
const content = ejs.render(await fs.readFile(controllerSpecTemplate, 'utf-8'), { ...config });
|
|
151
|
+
await fs.writeFile(controllerSpecPath, content);
|
|
152
|
+
await fs.remove(path.join(targetDir, 'src/interfaces/controllers', `${controllerSpecName}.ejs`));
|
|
153
|
+
}
|
|
119
154
|
}
|
|
120
155
|
// Render Server (Clean Arch JS only)
|
|
121
156
|
if (architecture === 'Clean Architecture' && language === 'JavaScript') {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
157
|
+
const serverName = 'server.js';
|
|
158
|
+
const serverPath = path.join(targetDir, 'src/infrastructure/webserver', serverName);
|
|
159
|
+
const serverTemplate = path.join(templatePath, 'src/infrastructure/webserver', `${serverName}.ejs`);
|
|
160
|
+
|
|
161
|
+
if (await fs.pathExists(serverTemplate)) {
|
|
162
|
+
const content = ejs.render(await fs.readFile(serverTemplate, 'utf-8'), { ...config });
|
|
163
|
+
await fs.writeFile(serverPath, content);
|
|
164
|
+
await fs.remove(path.join(targetDir, 'src/infrastructure/webserver', `${serverName}.ejs`));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Cleanup REST routes if GraphQL or Kafka is selected
|
|
169
|
+
if (config.communication !== 'REST APIs') {
|
|
170
|
+
if (architecture === 'MVC') {
|
|
171
|
+
await fs.remove(path.join(targetDir, 'src/routes'));
|
|
172
|
+
} else if (architecture === 'Clean Architecture') {
|
|
173
|
+
await fs.remove(path.join(targetDir, 'src/interfaces/routes'));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Advanced Health Check Route Modularization
|
|
178
|
+
const healthExt = language === 'TypeScript' ? 'ts' : 'js';
|
|
179
|
+
const healthTemplatePath = path.join(templatePath, '../../common/health', healthExt, `healthRoute.${healthExt}.ejs`);
|
|
180
|
+
|
|
181
|
+
if (await fs.pathExists(healthTemplatePath)) {
|
|
182
|
+
let routeDestDir = path.join(targetDir, 'src', 'routes');
|
|
183
|
+
if (architecture === 'Clean Architecture') {
|
|
184
|
+
routeDestDir = path.join(targetDir, 'src', 'interfaces', 'routes');
|
|
185
|
+
}
|
|
186
|
+
await fs.ensureDir(routeDestDir);
|
|
187
|
+
|
|
188
|
+
const healthRouteContent = ejs.render(await fs.readFile(healthTemplatePath, 'utf-8'), { ...config });
|
|
189
|
+
await fs.writeFile(path.join(routeDestDir, `healthRoute.${healthExt}`), healthRouteContent);
|
|
190
|
+
|
|
191
|
+
// Render health route spec template
|
|
192
|
+
const healthSpecTemplatePath = path.join(templatePath, '../../common/health', healthExt, `healthRoute.spec.${healthExt}.ejs`);
|
|
193
|
+
if (await fs.pathExists(healthSpecTemplatePath)) {
|
|
194
|
+
let testRouteDestDir = path.join(targetDir, 'tests', 'routes');
|
|
195
|
+
if (architecture === 'Clean Architecture') {
|
|
196
|
+
testRouteDestDir = path.join(targetDir, 'tests', 'interfaces', 'routes');
|
|
197
|
+
}
|
|
198
|
+
await fs.ensureDir(testRouteDestDir);
|
|
199
|
+
const specContent = ejs.render(await fs.readFile(healthSpecTemplatePath, 'utf-8'), config);
|
|
200
|
+
await fs.writeFile(path.join(testRouteDestDir, `healthRoute.spec.${healthExt}`), specContent);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Advanced Graceful Shutdown Modularization
|
|
205
|
+
const shutdownExt = language === 'TypeScript' ? 'ts' : 'js';
|
|
206
|
+
const shutdownTemplatePath = path.join(templatePath, '../../common/shutdown', shutdownExt, `gracefulShutdown.${shutdownExt}.ejs`);
|
|
207
|
+
|
|
208
|
+
if (await fs.pathExists(shutdownTemplatePath)) {
|
|
209
|
+
let utilsDestDir = path.join(targetDir, 'src', 'utils');
|
|
210
|
+
await fs.ensureDir(utilsDestDir);
|
|
211
|
+
|
|
212
|
+
const shutdownContent = ejs.render(await fs.readFile(shutdownTemplatePath, 'utf-8'), { ...config });
|
|
213
|
+
await fs.writeFile(path.join(utilsDestDir, `gracefulShutdown.${shutdownExt}`), shutdownContent);
|
|
214
|
+
|
|
215
|
+
// Render graceful shutdown spec template
|
|
216
|
+
const shutdownSpecTemplatePath = path.join(templatePath, '../../common/shutdown', shutdownExt, `gracefulShutdown.spec.${shutdownExt}.ejs`);
|
|
217
|
+
if (await fs.pathExists(shutdownSpecTemplatePath)) {
|
|
218
|
+
const testUtilsDestDir = path.join(targetDir, 'tests', 'utils');
|
|
219
|
+
await fs.ensureDir(testUtilsDestDir);
|
|
220
|
+
const specContent = ejs.render(await fs.readFile(shutdownSpecTemplatePath, 'utf-8'), config);
|
|
221
|
+
await fs.writeFile(path.join(testUtilsDestDir, `gracefulShutdown.spec.${shutdownExt}`), specContent);
|
|
222
|
+
}
|
|
131
223
|
}
|
|
132
224
|
|
|
133
225
|
// GraphQL Setup
|
|
134
226
|
if (config.communication === 'GraphQL') {
|
|
135
227
|
const ext = language === 'TypeScript' ? 'ts' : 'js';
|
|
136
228
|
let graphqlInternalPath = architecture === 'MVC' ? 'src/graphql' : 'src/interfaces/graphql';
|
|
137
|
-
|
|
229
|
+
|
|
138
230
|
const sourceGraphqDir = path.join(templatePath, graphqlInternalPath);
|
|
139
231
|
const targetGraphqlDir = path.join(targetDir, graphqlInternalPath);
|
|
140
232
|
|
|
@@ -169,27 +261,18 @@ export const renderDynamicComponents = async (templatePath, targetDir, config) =
|
|
|
169
261
|
let graphqlInternalPath = architecture === 'MVC' ? 'src/graphql' : 'src/interfaces/graphql';
|
|
170
262
|
await fs.remove(path.join(targetDir, graphqlInternalPath));
|
|
171
263
|
}
|
|
172
|
-
|
|
173
|
-
// Cleanup REST routes if GraphQL or Kafka is selected
|
|
174
|
-
if (config.communication !== 'REST APIs') {
|
|
175
|
-
if (architecture === 'MVC') {
|
|
176
|
-
await fs.remove(path.join(targetDir, 'src/routes'));
|
|
177
|
-
} else if (architecture === 'Clean Architecture') {
|
|
178
|
-
await fs.remove(path.join(targetDir, 'src/interfaces/routes'));
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
264
|
};
|
|
182
265
|
|
|
183
266
|
export const renderSwaggerConfig = async (templatesDir, targetDir, config) => {
|
|
184
267
|
const { communication, projectName, architecture, language } = config;
|
|
185
|
-
|
|
268
|
+
|
|
186
269
|
// Check for Swagger config template (typically in src/config/swagger.ts.ejs)
|
|
187
270
|
const swaggerTsTemplate = path.join(targetDir, 'src', 'config', 'swagger.ts.ejs');
|
|
188
271
|
// MVC JS uses swagger.js.ejs in src/config/
|
|
189
272
|
const swaggerJsTemplate = path.join(targetDir, 'src', 'config', 'swagger.js.ejs');
|
|
190
273
|
// Clean Arch JS uses swagger.js.ejs in src/infrastructure/webserver/
|
|
191
274
|
const swaggerJsCleanTemplate = path.join(targetDir, 'src', 'infrastructure', 'webserver', 'swagger.js.ejs');
|
|
192
|
-
|
|
275
|
+
|
|
193
276
|
// Ensure config directory exists
|
|
194
277
|
let configDir = path.join(targetDir, 'src', 'config');
|
|
195
278
|
if (architecture === 'Clean Architecture' && language === 'JavaScript') {
|
|
@@ -221,7 +304,7 @@ export const renderSwaggerConfig = async (templatesDir, targetDir, config) => {
|
|
|
221
304
|
await fs.writeFile(path.join(targetDir, 'src', 'infrastructure', 'webserver', 'swagger.js'), content);
|
|
222
305
|
}
|
|
223
306
|
}
|
|
224
|
-
|
|
307
|
+
|
|
225
308
|
// Always remove the .ejs template after processing (or if non-REST, just delete it)
|
|
226
309
|
if (await fs.pathExists(swaggerTsTemplate)) await fs.remove(swaggerTsTemplate);
|
|
227
310
|
if (await fs.pathExists(swaggerJsTemplate)) await fs.remove(swaggerJsTemplate);
|
|
@@ -239,7 +322,44 @@ export const setupViews = async (templatesDir, targetDir, config) => {
|
|
|
239
322
|
if (architecture === 'MVC' && viewEngine && viewEngine !== 'None') {
|
|
240
323
|
const viewsSource = path.join(templatesDir, 'common', 'views', viewEngine.toLowerCase());
|
|
241
324
|
if (await fs.pathExists(viewsSource)) {
|
|
242
|
-
|
|
325
|
+
await fs.copy(viewsSource, path.join(targetDir, 'src/views'));
|
|
243
326
|
}
|
|
244
327
|
}
|
|
245
328
|
};
|
|
329
|
+
|
|
330
|
+
export const processAllTests = async (targetDir, config) => {
|
|
331
|
+
const srcDir = path.join(targetDir, 'src');
|
|
332
|
+
const testsDir = path.join(targetDir, 'tests');
|
|
333
|
+
|
|
334
|
+
const processDir = async (currentDir) => {
|
|
335
|
+
if (!(await fs.pathExists(currentDir))) return;
|
|
336
|
+
const items = await fs.readdir(currentDir);
|
|
337
|
+
for (const item of items) {
|
|
338
|
+
|
|
339
|
+
const itemPath = path.join(currentDir, item);
|
|
340
|
+
const stat = await fs.stat(itemPath);
|
|
341
|
+
if (stat.isDirectory()) {
|
|
342
|
+
await processDir(itemPath);
|
|
343
|
+
} else if (itemPath.endsWith('.spec.ts') ||
|
|
344
|
+
itemPath.endsWith('.spec.js') ||
|
|
345
|
+
itemPath.endsWith('.spec.ts.ejs') ||
|
|
346
|
+
itemPath.endsWith('.spec.js.ejs')) {
|
|
347
|
+
const relativePath = path.relative(srcDir, itemPath);
|
|
348
|
+
|
|
349
|
+
const cleanRelativePath = relativePath.replace(/\.ejs$/, '');
|
|
350
|
+
|
|
351
|
+
const targetTestPath = path.join(testsDir, cleanRelativePath);
|
|
352
|
+
|
|
353
|
+
await fs.ensureDir(path.dirname(targetTestPath));
|
|
354
|
+
|
|
355
|
+
const template = await fs.readFile(itemPath, 'utf-8');
|
|
356
|
+
const content = ejs.render(template, config);
|
|
357
|
+
|
|
358
|
+
await fs.writeFile(targetTestPath, content);
|
|
359
|
+
await fs.remove(itemPath);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
await processDir(srcDir);
|
|
365
|
+
};
|
|
@@ -55,6 +55,19 @@ export const setupCaching = async (templatesDir, targetDir, config) => {
|
|
|
55
55
|
const cacheTemplate = await fs.readFile(cacheSource, 'utf-8');
|
|
56
56
|
const content = ejs.render(cacheTemplate, { loggerPath });
|
|
57
57
|
await fs.writeFile(cacheTarget, content);
|
|
58
|
+
|
|
59
|
+
// Render Spec if exists
|
|
60
|
+
const specTemplateName = clientObj.replace(`.${langExt}`, `.spec.${langExt}.ejs`);
|
|
61
|
+
const specTemplateSource = path.join(templatesDir, 'common', 'caching', langExt, specTemplateName);
|
|
62
|
+
if (await fs.pathExists(specTemplateSource)) {
|
|
63
|
+
const specTemplate = await fs.readFile(specTemplateSource, 'utf-8');
|
|
64
|
+
const specLoggerPath = architecture === 'Clean Architecture' ? '@/infrastructure/log/logger' : '@/utils/logger';
|
|
65
|
+
const specRedisPath = architecture === 'Clean Architecture' ? '@/infrastructure/caching/redisClient' : '@/config/redisClient';
|
|
66
|
+
const specContent = ejs.render(specTemplate, { ...config, loggerPath: specLoggerPath, redisClientPath: specRedisPath });
|
|
67
|
+
const specTarget = cacheTarget.replace(`${path.sep}src${path.sep}`, `${path.sep}tests${path.sep}`).replace(`.${langExt}`, `.spec.${langExt}`);
|
|
68
|
+
await fs.ensureDir(path.dirname(specTarget));
|
|
69
|
+
await fs.writeFile(specTarget, specContent);
|
|
70
|
+
}
|
|
58
71
|
}
|
|
59
72
|
}
|
|
60
73
|
};
|
|
@@ -3,127 +3,90 @@ import path from 'path';
|
|
|
3
3
|
import ejs from 'ejs';
|
|
4
4
|
|
|
5
5
|
export const renderPackageJson = async (templatesDir, targetDir, config) => {
|
|
6
|
-
const { projectName, database, communication, language, viewEngine, caching } = config;
|
|
7
6
|
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
8
7
|
const packageTemplate = await fs.readFile(path.join(templatesDir, 'common', 'package.json.ejs'), 'utf-8');
|
|
9
|
-
const packageContent = ejs.render(packageTemplate, {
|
|
10
|
-
projectName,
|
|
11
|
-
database,
|
|
12
|
-
communication,
|
|
13
|
-
language,
|
|
14
|
-
communication,
|
|
15
|
-
language,
|
|
16
|
-
viewEngine,
|
|
17
|
-
caching
|
|
18
|
-
});
|
|
8
|
+
const packageContent = ejs.render(packageTemplate, { ...config });
|
|
19
9
|
await fs.writeFile(packageJsonPath, packageContent);
|
|
20
10
|
};
|
|
21
11
|
|
|
22
12
|
export const renderDockerCompose = async (templatesDir, targetDir, config) => {
|
|
23
|
-
const { projectName, database, dbName, communication, caching } = config;
|
|
24
13
|
const dockerComposePath = path.join(targetDir, 'docker-compose.yml');
|
|
25
14
|
const dockerTemplate = await fs.readFile(path.join(templatesDir, 'common', 'docker-compose.yml.ejs'), 'utf-8');
|
|
26
|
-
const dockerContent = ejs.render(dockerTemplate, {
|
|
27
|
-
projectName,
|
|
28
|
-
database,
|
|
29
|
-
dbName,
|
|
30
|
-
communication,
|
|
31
|
-
caching
|
|
32
|
-
});
|
|
15
|
+
const dockerContent = ejs.render(dockerTemplate, { ...config });
|
|
33
16
|
await fs.writeFile(dockerComposePath, dockerContent);
|
|
34
17
|
};
|
|
35
18
|
|
|
36
19
|
export const renderReadme = async (templatesDir, targetDir, config) => {
|
|
37
|
-
const { projectName, architecture, database, communication, language, ciProvider, caching } = config;
|
|
38
20
|
const readmePath = path.join(targetDir, 'README.md');
|
|
39
21
|
const readmeTemplate = await fs.readFile(path.join(templatesDir, 'common', 'README.md.ejs'), 'utf-8');
|
|
40
|
-
const readmeContent = ejs.render(readmeTemplate, {
|
|
41
|
-
projectName,
|
|
42
|
-
architecture,
|
|
43
|
-
database,
|
|
44
|
-
communication,
|
|
45
|
-
caching,
|
|
46
|
-
language,
|
|
47
|
-
ciProvider
|
|
48
|
-
});
|
|
22
|
+
const readmeContent = ejs.render(readmeTemplate, { ...config });
|
|
49
23
|
await fs.writeFile(readmePath, readmeContent);
|
|
50
24
|
};
|
|
51
25
|
|
|
52
26
|
export const renderDockerfile = async (templatesDir, targetDir, config) => {
|
|
53
|
-
const { language, viewEngine } = config;
|
|
54
27
|
const dockerfileTemplate = await fs.readFile(path.join(templatesDir, 'common', 'Dockerfile'), 'utf-8');
|
|
55
|
-
const dockerfileContent = ejs.render(dockerfileTemplate, {
|
|
56
|
-
language,
|
|
57
|
-
viewEngine
|
|
58
|
-
});
|
|
28
|
+
const dockerfileContent = ejs.render(dockerfileTemplate, { ...config });
|
|
59
29
|
await fs.writeFile(path.join(targetDir, 'Dockerfile'), dockerfileContent);
|
|
60
30
|
};
|
|
61
31
|
|
|
62
32
|
export const renderPm2Config = async (templatesDir, targetDir, config) => {
|
|
63
|
-
const { projectName, database, dbName, communication, language, caching } = config;
|
|
64
33
|
const pm2ConfigPath = path.join(targetDir, 'ecosystem.config.js');
|
|
65
34
|
const pm2Template = await fs.readFile(path.join(templatesDir, 'common', 'ecosystem.config.js.ejs'), 'utf-8');
|
|
66
|
-
const pm2Content = ejs.render(pm2Template, {
|
|
67
|
-
projectName,
|
|
68
|
-
database,
|
|
69
|
-
dbName,
|
|
70
|
-
communication,
|
|
71
|
-
language,
|
|
72
|
-
caching
|
|
73
|
-
});
|
|
35
|
+
const pm2Content = ejs.render(pm2Template, { ...config });
|
|
74
36
|
await fs.writeFile(pm2ConfigPath, pm2Content);
|
|
75
37
|
};
|
|
76
38
|
|
|
77
|
-
export const renderProfessionalConfig = async (templatesDir, targetDir,
|
|
39
|
+
export const renderProfessionalConfig = async (templatesDir, targetDir, config) => {
|
|
78
40
|
const eslintTemplate = await fs.readFile(path.join(templatesDir, 'common', 'eslint.config.mjs.ejs'), 'utf-8');
|
|
79
|
-
const eslintContent = ejs.render(eslintTemplate, {
|
|
41
|
+
const eslintContent = ejs.render(eslintTemplate, { ...config });
|
|
80
42
|
await fs.writeFile(path.join(targetDir, 'eslint.config.mjs'), eslintContent);
|
|
81
43
|
|
|
82
44
|
await fs.copy(path.join(templatesDir, 'common', '.prettierrc'), path.join(targetDir, '.prettierrc'));
|
|
83
45
|
await fs.copy(path.join(templatesDir, 'common', '.lintstagedrc'), path.join(targetDir, '.lintstagedrc'));
|
|
84
46
|
|
|
85
47
|
const jestTemplate = await fs.readFile(path.join(templatesDir, 'common', 'jest.config.js.ejs'), 'utf-8');
|
|
86
|
-
const jestContent = ejs.render(jestTemplate, {
|
|
48
|
+
const jestContent = ejs.render(jestTemplate, { ...config });
|
|
87
49
|
await fs.writeFile(path.join(targetDir, 'jest.config.js'), jestContent);
|
|
88
50
|
};
|
|
89
51
|
|
|
90
52
|
export const setupCiCd = async (templatesDir, targetDir, config) => {
|
|
91
|
-
const { ciProvider
|
|
53
|
+
const { ciProvider } = config;
|
|
92
54
|
if (ciProvider === 'GitHub Actions') {
|
|
93
55
|
await fs.ensureDir(path.join(targetDir, '.github/workflows'));
|
|
94
56
|
await fs.copy(path.join(templatesDir, 'common', '_github/workflows/ci.yml'), path.join(targetDir, '.github/workflows/ci.yml'));
|
|
95
57
|
} else if (ciProvider === 'Jenkins') {
|
|
96
58
|
const jenkinsTemplate = await fs.readFile(path.join(templatesDir, 'common', 'Jenkinsfile.ejs'), 'utf-8');
|
|
97
|
-
const jenkinsContent = ejs.render(jenkinsTemplate, {
|
|
59
|
+
const jenkinsContent = ejs.render(jenkinsTemplate, { ...config });
|
|
98
60
|
await fs.writeFile(path.join(targetDir, 'Jenkinsfile'), jenkinsContent);
|
|
99
61
|
} else if (ciProvider === 'GitLab CI') {
|
|
100
62
|
const gitlabTemplate = await fs.readFile(path.join(templatesDir, 'common', '.gitlab-ci.yml.ejs'), 'utf-8');
|
|
101
|
-
const gitlabContent = ejs.render(gitlabTemplate, {
|
|
63
|
+
const gitlabContent = ejs.render(gitlabTemplate, { ...config });
|
|
102
64
|
await fs.writeFile(path.join(targetDir, '.gitlab-ci.yml'), gitlabContent);
|
|
103
65
|
}
|
|
104
66
|
};
|
|
105
67
|
|
|
106
|
-
export const renderTestSample = async (templatesDir, targetDir,
|
|
68
|
+
export const renderTestSample = async (templatesDir, targetDir, config) => {
|
|
107
69
|
await fs.ensureDir(path.join(targetDir, 'tests'));
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
70
|
+
if (config.language === 'TypeScript') {
|
|
71
|
+
const testsTsConfig = {
|
|
72
|
+
"extends": "../tsconfig.json",
|
|
73
|
+
"compilerOptions": {
|
|
74
|
+
"types": ["jest", "node"],
|
|
75
|
+
"rootDir": "../tests",
|
|
76
|
+
"noEmit": true
|
|
77
|
+
},
|
|
78
|
+
"include": ["**/*.ts"]
|
|
79
|
+
};
|
|
80
|
+
await fs.writeFile(path.join(targetDir, 'tests', 'tsconfig.json'), JSON.stringify(testsTsConfig, null, 4));
|
|
81
|
+
}
|
|
112
82
|
};
|
|
113
83
|
|
|
114
84
|
export const renderEnvExample = async (templatesDir, targetDir, config) => {
|
|
115
|
-
const { database, dbName, communication, projectName, caching } = config;
|
|
116
85
|
const envExamplePath = path.join(targetDir, '.env.example');
|
|
117
86
|
const envPath = path.join(targetDir, '.env');
|
|
118
87
|
const envTemplate = await fs.readFile(path.join(templatesDir, 'common', '.env.example.ejs'), 'utf-8');
|
|
119
88
|
|
|
120
|
-
const envContent = ejs.render(envTemplate, {
|
|
121
|
-
database,
|
|
122
|
-
dbName,
|
|
123
|
-
communication,
|
|
124
|
-
projectName,
|
|
125
|
-
caching
|
|
126
|
-
});
|
|
89
|
+
const envContent = ejs.render(envTemplate, { ...config });
|
|
127
90
|
|
|
128
91
|
await fs.writeFile(envExamplePath, envContent);
|
|
129
92
|
await fs.writeFile(envPath, envContent);
|