nodejs-quickstart-structure 1.9.2 → 1.9.4

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 CHANGED
@@ -5,6 +5,20 @@ 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.9.4] - 2026-02-26
9
+ ### Fixed
10
+ - Fixed an `EBADENGINE` compatibility issue during Docker builds by upgrading the default template `Dockerfile` base image from `node:18-alpine` to `node:22-alpine`, supporting modern dependencies like `eslint@9` and `cpx2`.
11
+ - Fixed a port collision bug (`Bind for 0.0.0.0:6379 failed: port is already allocated`) during parallel E2E testing. The `validation-core.js` script now dynamically assigns random network ports for `REDIS_PORT` when running concurrent testing matrices.
12
+
13
+ ## [1.9.3] - 2026-02-26
14
+ ### Added
15
+ - Refactored Swagger documentation to use a standalone `swagger.yml` file instead of inline JSDoc comments across all templates (MVC, Clean Architecture, TS, and JS).
16
+ - Integrated `yamljs` to parse the `swagger.yml` file and removed the `swagger-jsdoc` dependency, significantly reducing boilerplate in route files.
17
+
18
+ ### Fixed
19
+ - Fixed an issue in `package.json.ejs` where `jest` configuration had malformed JSON due to whitespace stripping.
20
+ - Fixed a Docker build crash for non-REST API projects caused by the build script unconditionally attempting to copy `swagger.yml` using `cpx2`.
21
+
8
22
  ## [1.9.2] - 2026-02-23
9
23
  ### Fixed
10
24
  - Fixed an issue where the generator output misleading instructions (`DATABASE_URL`) for standalone `docker run` executions. The CLI success commands and `README.md` now conditionally include dynamic compose network bindings (`--network`) and accurate environment variables matching the user's selected DB stack.
package/lib/generator.js CHANGED
@@ -58,7 +58,7 @@ export const generateProject = async (config) => {
58
58
  await setupSrcViews(templatesDir, targetDir, config);
59
59
 
60
60
  // 12. Swagger Config
61
- await renderSwaggerConfig(targetDir, config);
61
+ await renderSwaggerConfig(templatesDir, targetDir, config);
62
62
 
63
63
  // 13. Professional Config & Tests
64
64
  await renderProfessionalConfig(templatesDir, targetDir, language);
@@ -63,24 +63,34 @@ export const renderDynamicComponents = async (templatePath, targetDir, config) =
63
63
  }
64
64
  };
65
65
 
66
- export const renderSwaggerConfig = async (targetDir, config) => {
67
- const { communication } = config;
66
+ export const renderSwaggerConfig = async (templatesDir, targetDir, config) => {
67
+ const { communication, projectName, architecture, language } = config;
68
68
 
69
- // Check for Swagger config template (typically in src/config/swagger.ts.ejs)
70
- // This path is common for both MVC and Clean Arch TS templates based on current structure
71
69
  // Check for Swagger config template (typically in src/config/swagger.ts.ejs)
72
70
  const swaggerTsTemplate = path.join(targetDir, 'src', 'config', 'swagger.ts.ejs');
73
71
 
74
72
  // Ensure config directory exists
75
- await fs.ensureDir(path.join(targetDir, 'src', 'config'));
73
+ let configDir = path.join(targetDir, 'src', 'config');
74
+ if (architecture === 'Clean Architecture' && language === 'JavaScript') {
75
+ configDir = path.join(targetDir, 'src', 'infrastructure', 'webserver');
76
+ }
77
+ await fs.ensureDir(configDir);
76
78
 
77
- if (await fs.pathExists(swaggerTsTemplate)) {
78
- // Render if REST APIs
79
- if (communication === 'REST APIs') {
79
+ if (communication === 'REST APIs') {
80
+ const swaggerYmlTemplateSource = path.join(templatesDir, 'common', 'swagger.yml.ejs');
81
+ if (await fs.pathExists(swaggerYmlTemplateSource)) {
82
+ const ymlContent = ejs.render(await fs.readFile(swaggerYmlTemplateSource, 'utf-8'), { projectName });
83
+ await fs.writeFile(path.join(configDir, 'swagger.yml'), ymlContent);
84
+ }
85
+
86
+ if (await fs.pathExists(swaggerTsTemplate)) {
80
87
  const content = ejs.render(await fs.readFile(swaggerTsTemplate, 'utf-8'), { communication });
81
88
  await fs.writeFile(path.join(targetDir, 'src', 'config', 'swagger.ts'), content);
82
89
  }
83
- // Always remove the template after processing or if not needed
90
+ }
91
+
92
+ // Always remove the template after processing or if not needed
93
+ if (await fs.pathExists(swaggerTsTemplate)) {
84
94
  await fs.remove(swaggerTsTemplate);
85
95
  }
86
96
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-quickstart-structure",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "type": "module",
5
5
  "description": "A CLI to scaffold Node.js microservices with MVC or Clean Architecture",
6
6
  "main": "bin/index.js",
@@ -1,23 +1,6 @@
1
- const swaggerJsdoc = require('swagger-jsdoc');
1
+ const path = require('path');
2
+ const YAML = require('yamljs');
2
3
 
3
- const options = {
4
- definition: {
5
- openapi: '3.0.0',
6
- info: {
7
- title: 'NodeJS API Service',
8
- version: '1.0.0',
9
- description: 'API documentation for the NodeJS Service',
10
- },
11
- servers: [
12
- {
13
- url: process.env.SERVER_URL || `http://localhost:${process.env.PORT || 3000}`,
14
- description: 'Server',
15
- },
16
- ],
17
- },
18
- apis: ['./src/interfaces/routes/*.js'], // Path to the API docs
19
- };
4
+ const swaggerDocument = YAML.load(path.join(__dirname, 'swagger.yml'));
20
5
 
21
- const specs = swaggerJsdoc(options);
22
-
23
- module.exports = specs;
6
+ module.exports = swaggerDocument;
@@ -2,75 +2,8 @@ const express = require('express');
2
2
  const router = express.Router();
3
3
  const UserController = require('../controllers/userController');
4
4
 
5
- // Should inject dependencies here in a real app
6
5
  const userController = new UserController();
7
6
 
8
- /**
9
- * @swagger
10
- * components:
11
- * schemas:
12
- * User:
13
- * type: object
14
- * required:
15
- * - name
16
- * - email
17
- * properties:
18
- * id:
19
- * type: integer
20
- * description: The auto-generated id of the user
21
- * name:
22
- * type: string
23
- * description: The name of the user
24
- * email:
25
- * type: string
26
- * description: The email of the user
27
- * example:
28
- * id: 1
29
- * name: John Doe
30
- * email: john@example.com
31
- */
32
-
33
- /**
34
- * @swagger
35
- * tags:
36
- * name: Users
37
- * description: The users managing API
38
- */
39
-
40
- /**
41
- * @swagger
42
- * /api/users:
43
- * get:
44
- * summary: Returns the list of all the users
45
- * tags: [Users]
46
- * responses:
47
- * 200:
48
- * description: The list of the users
49
- * content:
50
- * application/json:
51
- * schema:
52
- * type: array
53
- * items:
54
- * $ref: '#/components/schemas/User'
55
- * post:
56
- * summary: Create a new user
57
- * tags: [Users]
58
- * requestBody:
59
- * required: true
60
- * content:
61
- * application/json:
62
- * schema:
63
- * $ref: '#/components/schemas/User'
64
- * responses:
65
- * 201:
66
- * description: The created user.
67
- * content:
68
- * application/json:
69
- * schema:
70
- * $ref: '#/components/schemas/User'
71
- * 500:
72
- * description: Some server error
73
- */
74
7
  router.post('/users', (req, res) => userController.createUser(req, res));
75
8
  router.get('/users', (req, res) => userController.getUsers(req, res));
76
9
 
@@ -1,23 +1,6 @@
1
- <% if (communication === 'REST APIs') { %>import swaggerJsdoc from 'swagger-jsdoc';
1
+ <% if (communication === 'REST APIs') { %>import path from 'path';
2
+ import YAML from 'yamljs';
2
3
 
3
- const options = {
4
- definition: {
5
- openapi: '3.0.0',
6
- info: {
7
- title: 'NodeJS API Service',
8
- version: '1.0.0',
9
- description: 'API documentation for the NodeJS Service',
10
- },
11
- servers: [
12
- {
13
- url: process.env.SERVER_URL || `http://localhost:${process.env.PORT || 3000}`,
14
- description: 'Server',
15
- },
16
- ],
17
- },
18
- apis: ['./src/interfaces/routes/*.ts', './dist/interfaces/routes/*.js'], // Path to the API docs
19
- };
4
+ const swaggerDocument = YAML.load(path.join(__dirname, 'swagger.yml'));
20
5
 
21
- const specs = swaggerJsdoc(options);
22
-
23
- export default specs;<% } %>
6
+ export default swaggerDocument;<% } %>
@@ -4,72 +4,6 @@ import { UserController } from '@/interfaces/controllers/userController';
4
4
  const router = Router();
5
5
  const userController = new UserController();
6
6
 
7
- /**
8
- * @swagger
9
- * components:
10
- * schemas:
11
- * User:
12
- * type: object
13
- * required:
14
- * - name
15
- * - email
16
- * properties:
17
- * id:
18
- * type: integer
19
- * description: The auto-generated id of the user
20
- * name:
21
- * type: string
22
- * description: The name of the user
23
- * email:
24
- * type: string
25
- * description: The email of the user
26
- * example:
27
- * id: 1
28
- * name: John Doe
29
- * email: john@example.com
30
- */
31
-
32
- /**
33
- * @swagger
34
- * tags:
35
- * name: Users
36
- * description: The users managing API
37
- */
38
-
39
- /**
40
- * @swagger
41
- * /api/users:
42
- * get:
43
- * summary: Returns the list of all the users
44
- * tags: [Users]
45
- * responses:
46
- * 200:
47
- * description: The list of the users
48
- * content:
49
- * application/json:
50
- * schema:
51
- * type: array
52
- * items:
53
- * $ref: '#/components/schemas/User'
54
- * post:
55
- * summary: Create a new user
56
- * tags: [Users]
57
- * requestBody:
58
- * required: true
59
- * content:
60
- * application/json:
61
- * schema:
62
- * $ref: '#/components/schemas/User'
63
- * responses:
64
- * 201:
65
- * description: The created user.
66
- * content:
67
- * application/json:
68
- * schema:
69
- * $ref: '#/components/schemas/User'
70
- * 500:
71
- * description: Some server error
72
- */
73
7
  router.post('/', (req: Request, res: Response) => userController.createUser(req, res));
74
8
  router.get('/', (req: Request, res: Response) => userController.getUsers(req, res));
75
9
 
@@ -1,7 +1,7 @@
1
1
  # ==========================================
2
2
  # Stage 1: Builder
3
3
  # ==========================================
4
- FROM node:18-alpine AS builder
4
+ FROM node:22-alpine AS builder
5
5
 
6
6
  WORKDIR /app
7
7
 
@@ -19,7 +19,7 @@ COPY . .
19
19
  # ==========================================
20
20
  # Stage 2: Production
21
21
  # ==========================================
22
- FROM node:18-alpine AS production
22
+ FROM node:22-alpine AS production
23
23
 
24
24
  WORKDIR /app
25
25
 
@@ -6,7 +6,7 @@
6
6
  "scripts": {
7
7
  "start": "<% if (language === 'TypeScript') { %>node dist/index.js<% } else { %>node src/index.js<% } %>",
8
8
  "dev": "<% if (language === 'TypeScript') { %>nodemon --exec ts-node -r tsconfig-paths/register src/index.ts<% } else { %>nodemon src/index.js<% } %>"<% if (language === 'TypeScript') { %>,
9
- "build": "rimraf dist && tsc && tsc-alias<% if (viewEngine && viewEngine !== 'None') { %> && cpx \"src/views/**/*\" dist/views<% } %>"<% } %>,
9
+ "build": "rimraf dist && tsc && tsc-alias<% if (viewEngine && viewEngine !== 'None') { %> && cpx \"src/views/**/*\" dist/views<% } %><% if (communication === 'REST APIs') { %> && cpx \"src/**/*.yml\" dist/<% } %>"<% } %>,
10
10
  "lint": "eslint .",
11
11
  "lint:fix": "eslint . --fix",
12
12
  "format": "prettier --write .",
@@ -48,7 +48,7 @@
48
48
  "winston-daily-rotate-file": "^5.0.0",
49
49
  "morgan": "^1.10.0"<% if (communication === 'REST APIs') { %>,
50
50
  "swagger-ui-express": "^5.0.0",
51
- "swagger-jsdoc": "^6.2.8"<% } %>
51
+ "yamljs": "^0.3.0"<% } %>
52
52
  },
53
53
  "devDependencies": {
54
54
  "nodemon": "^3.0.2"<% if (language === 'TypeScript') { %>,
@@ -68,7 +68,7 @@
68
68
  "@types/sequelize": "^4.28.19",
69
69
  <%_ } -%>
70
70
  "@types/morgan": "^1.9.9",
71
- "rimraf": "^6.0.1"<% if (viewEngine && viewEngine !== 'None') { %>,
71
+ "rimraf": "^6.0.1"<% if ((viewEngine && viewEngine !== 'None') || communication === 'REST APIs') { %>,
72
72
  "cpx2": "^8.0.0"<% } %><% } %>,
73
73
  "eslint": "^9.20.1",
74
74
  "@eslint/js": "^9.20.0",
@@ -79,7 +79,7 @@
79
79
  "lint-staged": "^15.4.3"<% if (language === 'TypeScript') { %>,
80
80
  "typescript-eslint": "^8.24.1",
81
81
  <% if (communication === 'REST APIs') { %> "@types/swagger-ui-express": "^4.1.6",
82
- "@types/swagger-jsdoc": "^6.0.4",<% } -%>
82
+ "@types/yamljs": "^0.2.34",<% } %>
83
83
  "jest": "^29.7.0",
84
84
  "ts-jest": "^29.2.5",
85
85
  "@types/jest": "^29.5.14",
@@ -0,0 +1,66 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: <%= projectName %> API
4
+ version: 1.0.0
5
+ description: API documentation for <%= projectName %>
6
+ servers:
7
+ - url: http://localhost:3000
8
+ description: Local Server
9
+ components:
10
+ schemas:
11
+ User:
12
+ type: object
13
+ required:
14
+ - name
15
+ - email
16
+ properties:
17
+ id:
18
+ type: integer
19
+ description: The auto-generated id of the user
20
+ name:
21
+ type: string
22
+ description: The name of the user
23
+ email:
24
+ type: string
25
+ description: The email of the user
26
+ example:
27
+ id: 1
28
+ name: John Doe
29
+ email: john@example.com
30
+ tags:
31
+ - name: Users
32
+ description: The users managing API
33
+ paths:
34
+ /api/users:
35
+ get:
36
+ summary: Returns the list of all the users
37
+ tags:
38
+ - Users
39
+ responses:
40
+ '200':
41
+ description: The list of the users
42
+ content:
43
+ application/json:
44
+ schema:
45
+ type: array
46
+ items:
47
+ $ref: '#/components/schemas/User'
48
+ post:
49
+ summary: Create a new user
50
+ tags:
51
+ - Users
52
+ requestBody:
53
+ required: true
54
+ content:
55
+ application/json:
56
+ schema:
57
+ $ref: '#/components/schemas/User'
58
+ responses:
59
+ '201':
60
+ description: The created user.
61
+ content:
62
+ application/json:
63
+ schema:
64
+ $ref: '#/components/schemas/User'
65
+ '500':
66
+ description: Some server error
@@ -1,23 +1,6 @@
1
- const swaggerJsdoc = require('swagger-jsdoc');
1
+ const path = require('path');
2
+ const YAML = require('yamljs');
2
3
 
3
- const options = {
4
- definition: {
5
- openapi: '3.0.0',
6
- info: {
7
- title: 'NodeJS API Service',
8
- version: '1.0.0',
9
- description: 'API documentation for the NodeJS Service',
10
- },
11
- servers: [
12
- {
13
- url: process.env.SERVER_URL || `http://localhost:${process.env.PORT || 3000}`,
14
- description: 'Server',
15
- },
16
- ],
17
- },
18
- apis: ['./src/routes/*.js'], // Path to the API docs
19
- };
4
+ const swaggerDocument = YAML.load(path.join(__dirname, 'swagger.yml'));
20
5
 
21
- const specs = swaggerJsdoc(options);
22
-
23
- module.exports = specs;
6
+ module.exports = swaggerDocument;
@@ -2,72 +2,6 @@ const express = require('express');
2
2
  const router = express.Router();
3
3
  const userController = require('../controllers/userController');
4
4
 
5
- /**
6
- * @swagger
7
- * components:
8
- * schemas:
9
- * User:
10
- * type: object
11
- * required:
12
- * - name
13
- * - email
14
- * properties:
15
- * id:
16
- * type: integer
17
- * description: The auto-generated id of the user
18
- * name:
19
- * type: string
20
- * description: The name of the user
21
- * email:
22
- * type: string
23
- * description: The email of the user
24
- * example:
25
- * id: 1
26
- * name: John Doe
27
- * email: john@example.com
28
- */
29
-
30
- /**
31
- * @swagger
32
- * tags:
33
- * name: Users
34
- * description: The users managing API
35
- */
36
-
37
- /**
38
- * @swagger
39
- * /api/users:
40
- * get:
41
- * summary: Returns the list of all the users
42
- * tags: [Users]
43
- * responses:
44
- * 200:
45
- * description: The list of the users
46
- * content:
47
- * application/json:
48
- * schema:
49
- * type: array
50
- * items:
51
- * $ref: '#/components/schemas/User'
52
- * post:
53
- * summary: Create a new user
54
- * tags: [Users]
55
- * requestBody:
56
- * required: true
57
- * content:
58
- * application/json:
59
- * schema:
60
- * $ref: '#/components/schemas/User'
61
- * responses:
62
- * 201:
63
- * description: The created user.
64
- * content:
65
- * application/json:
66
- * schema:
67
- * $ref: '#/components/schemas/User'
68
- * 500:
69
- * description: Some server error
70
- */
71
5
  router.get('/users', userController.getUsers);
72
6
  router.post('/users', userController.createUser);
73
7
 
@@ -1,23 +1,6 @@
1
- <% if (communication === 'REST APIs') { %>import swaggerJsdoc from 'swagger-jsdoc';
1
+ <% if (communication === 'REST APIs') { %>import path from 'path';
2
+ import YAML from 'yamljs';
2
3
 
3
- const options = {
4
- definition: {
5
- openapi: '3.0.0',
6
- info: {
7
- title: 'NodeJS API Service',
8
- version: '1.0.0',
9
- description: 'API documentation for the NodeJS Service',
10
- },
11
- servers: [
12
- {
13
- url: process.env.SERVER_URL || `http://localhost:${process.env.PORT || 3000}`,
14
- description: 'Server',
15
- },
16
- ],
17
- },
18
- apis: ['./src/routes/*.ts', './dist/routes/*.js'], // Path to the API docs
19
- };
4
+ const swaggerDocument = YAML.load(path.join(__dirname, 'swagger.yml'));
20
5
 
21
- const specs = swaggerJsdoc(options);
22
-
23
- export default specs;<% } %>
6
+ export default swaggerDocument;<% } %>
@@ -4,72 +4,6 @@ import { UserController } from '@/controllers/userController';
4
4
  const router = Router();
5
5
  const userController = new UserController();
6
6
 
7
- /**
8
- * @swagger
9
- * components:
10
- * schemas:
11
- * User:
12
- * type: object
13
- * required:
14
- * - name
15
- * - email
16
- * properties:
17
- * id:
18
- * type: integer
19
- * description: The auto-generated id of the user
20
- * name:
21
- * type: string
22
- * description: The name of the user
23
- * email:
24
- * type: string
25
- * description: The email of the user
26
- * example:
27
- * id: 1
28
- * name: John Doe
29
- * email: john@example.com
30
- */
31
-
32
- /**
33
- * @swagger
34
- * tags:
35
- * name: Users
36
- * description: The users managing API
37
- */
38
-
39
- /**
40
- * @swagger
41
- * /api/users:
42
- * get:
43
- * summary: Returns the list of all the users
44
- * tags: [Users]
45
- * responses:
46
- * 200:
47
- * description: The list of the users
48
- * content:
49
- * application/json:
50
- * schema:
51
- * type: array
52
- * items:
53
- * $ref: '#/components/schemas/User'
54
- * post:
55
- * summary: Create a new user
56
- * tags: [Users]
57
- * requestBody:
58
- * required: true
59
- * content:
60
- * application/json:
61
- * schema:
62
- * $ref: '#/components/schemas/User'
63
- * responses:
64
- * 201:
65
- * description: The created user.
66
- * content:
67
- * application/json:
68
- * schema:
69
- * $ref: '#/components/schemas/User'
70
- * 500:
71
- * description: Some server error
72
- */
73
7
  router.get('/users', (req: Request, res: Response) => userController.getUsers(req, res));
74
8
  router.post('/users', (req: Request, res: Response) => userController.createUser(req, res));
75
9